luogu 3 月月赛 I & EE Round 1 Div.1

这两天写题越来越有感觉了。

Div.1 A

一通乱猜,得到结论:当没有 (1) 的时候,答案是 (sum a_ia_{i+1}+min a_i)

也就是从左往右消到最小值的左边,然后从右往左消到最小值的右边,最后消掉最小值。

对原序列每个没有 (1) 的极长子段按刚才的式子算一下,然后把 (1) 的个数加上去,就好了。

证明不难。

Div.1 B

这是大力出奇迹题,考场不敢开大数组获得整整 30 分!

打表可知,(p|a_p) 当且仅当 (p|(k+1))

证明:

不会,咕咕咕。

n 以内除给定数以外所有质数的乘积就是答案,线筛就好。

据说 min25 能筛出来……先咕着。

Div.1 C

这题挺好的。

先考虑没有 (-1) 的情况怎么做。

首先根据输入的信息,容易知道到每个位置的时候单调栈里面都有哪些下标。

考虑下标 (i<j_1<j_2<cdots <j_k) 中,(j_1,j_2,cdots,j_k) 曾经先后在单调栈里恰好压在 (i) 头顶上。

那么必有 (p_i<p_{j_k}<p_{j_{k-1}}<cdots <p_{j_1})

而只要一个排列满足以上条件,那么这个排列就一定是符合题意的。

考虑构造。从 (0) 向在栈底待过的下标连边,对每个 (i) ,从 (i) 按顺序向 (j_1,j_2,cdots,j_k) 连边。

你发现这样构成了一棵 ((n+1)) 个点的树。这棵树满足父亲小于儿子,且 对同一个父亲 编号大的儿子小于编号小的儿子。

考虑到要字典序最小,那么编号小的 (i) 所获得的 (p_i) 要更小。

对这棵树 DFS 一遍,每到一个点,从后往前确定每个儿子填什么,然后从前往后进儿子,递归执行。(具体参考代码)

似乎还没说 (-1) 怎么做。设 (a_i=-1) ,那 (a_ileftarrow a_{i-1}+1) 就好了。容易证明这样没有问题。

#include<cstdio>
#include<vector>
const int N=1e6+3;
int n,a[N],st[N],s[N],k;
std::vector<int>g[N];
void Dfs(int i){
	if(g[i].size())for(int j=g[i].size()-1;~j;j--)s[g[i][j]]=++k;
	for(int j=0;j<g[i].size();j++){
	  Dfs(g[i][j]);
	}
}
signed main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
	  scanf("%d",a+i);
	  if(a[i]<0)a[i]=a[i-1]+1;
	}
	for(int i=1;i<=n;i++){
	  for(;k+1>a[i];--k);
	  g[st[k]].push_back(i);
	  st[++k]=i;
	}
	k=0;
	Dfs(0);
	for(int i=1;i<=n;i++)printf("%d ",s[i]);puts("");
	return 0;
}
原文地址:https://www.cnblogs.com/Camp-Nou/p/12444000.html