这两天写题越来越有感觉了。
一通乱猜,得到结论:当没有 (1) 的时候,答案是 (sum a_ia_{i+1}+min a_i)。
也就是从左往右消到最小值的左边,然后从右往左消到最小值的右边,最后消掉最小值。
对原序列每个没有 (1) 的极长子段按刚才的式子算一下,然后把 (1) 的个数加上去,就好了。
证明不难。
这是大力出奇迹题,考场不敢开大数组获得整整 30 分!
打表可知,(p|a_p) 当且仅当 (p|(k+1))。
证明:
不会,咕咕咕。
n 以内除给定数以外所有质数的乘积就是答案,线筛就好。
据说 min25 能筛出来……先咕着。
这题挺好的。
先考虑没有 (-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;
}