[AGC003E] Sequential operations on Sequence
题目大意
一串数,初始为 \(1\sim n\),现在给 \(Q\) 个操作,每次操作把数组长度变为 \(q_i\),新增的数为上一个操作后的数组的重复。问 \(Q\) 次操作后 \(1\sim n\) 每个数出现了多少次。
解析
对于一个操作,如果这个操作之后存在长度小于自己的操作,那么该操作是无效的。所以我们可以先用单调栈将所有操作变成单调递增的。
不妨倒过来考虑。我们对每一个操作都记录一个系数 \(a_i\),代表这个操作对答案的贡献会乘上多少倍。对于第 \(i\) 个操作 \(q_i\),设 \(t=\frac{q_i}{q_{i-1}}\),那么 \(a_{i-1}\) 需要加上 \(t\times a_i\) ,表示前面重复了 \(t\) 次。然后对于剩下 \(q_{i}\bmod q_{i-1}\),我们需要二分出长度最大的小于 \(q_i\bmod q_{i-1}\) 的操作 \(p\),那么这多余的一段就是 \(p\) 的重复,并且又会产生一段剩余。我们这样递归下去,直到找不出大于当前长度 \(len\) 的操作。此时,直接对答案区间 \([1,len]\) 加上 \(q_i\) (如果 \(q_i=0\) 就直接加 \(1\) )。注意在运算过程中不要乘爆 long long
。
代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#define int long long
#define N 100002
using namespace std;
int n,q,i,a[N],s[N],top,cnt[N],ans[N];
int read()
{
char c=getchar();
int w=0;
while(c<'0'||c>'9') c=getchar();
while(c<='9'&&c>='0'){
w=w*10+c-'0';
c=getchar();
}
return w;
}
void solve(int x,int l)
{
int pos=upper_bound(s+1,s+top+1,l)-s-1;
if(pos==0){
if(cnt[x]==0) ans[1]++,ans[l+1]--;
else ans[1]+=cnt[x];ans[l+1]-=cnt[x];
return;
}
cnt[pos]+=l/s[pos]*(cnt[x]==0?1:cnt[x]);
solve(x,l%s[pos]);
}
signed main()
{
n=read();q=read();
for(i=1;i<=q;i++) a[i]=read();
s[++top]=n;
for(i=1;i<=q;i++){
while(top&&s[top]>=a[i]) top--;
s[++top]=a[i];
}
for(i=top;i>1;i--){
cnt[i-1]+=s[i]/s[i-1]*(cnt[i]==0?1:cnt[i]);
solve(i,s[i]%s[i-1]);
}
if(cnt[1]==0) ans[1]++,ans[s[1]+1]--;
else ans[1]+=cnt[1];ans[s[1]+1]-=cnt[1];
for(i=1;i<=n;i++) ans[i]+=ans[i-1];
for(i=1;i<=n;i++) printf("%lld\n",ans[i]);
return 0;
}