[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;
}
posted @ 2020-11-22 22:15  CJlzf  阅读(98)  评论(0编辑  收藏  举报