[AGC003E] Sequential operations on Sequence

题目大意

一串数,初始为 (1sim n),现在给 (Q) 个操作,每次操作把数组长度变为 (q_i),新增的数为上一个操作后的数组的重复。问 (Q) 次操作后 (1sim n) 每个数出现了多少次。

解析

对于一个操作,如果这个操作之后存在长度小于自己的操作,那么该操作是无效的。所以我们可以先用单调栈将所有操作变成单调递增的。

不妨倒过来考虑。我们对每一个操作都记录一个系数 (a_i),代表这个操作对答案的贡献会乘上多少倍。对于第 (i) 个操作 (q_i),设 (t=frac{q_i}{q_{i-1}}),那么 (a_{i-1}) 需要加上 (t imes a_i) ,表示前面重复了 (t) 次。然后对于剩下 (q_{i}mod q_{i-1}),我们需要二分出长度最大的小于 (q_imod 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
",ans[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/LSlzf/p/14021873.html