AT2005-[AGC003E]Sequential operations on Sequence【差分,思维】

正题

题目链接:https://www.luogu.com.cn/problem/AT2005


题目大意

开始有一个\(1\sim n\)依次排列的序列,然后\(Q\)次,第\(i\)次把序列长度变为\(a_i\),不足的从前往后循环填充。

求最后每个数字的出现次数。

\(1\leq n,q\leq 10^5,1\leq a_i\leq 10^{18}\)


解题思路

首先肯定是先搞出一个单调栈来,然后考虑每次复制重复的部分。

考虑第\(i\)次,首先是原先的序列重复\(\lfloor\frac{a_i}{a_{i-1}}\rfloor\)次,然后后面会剩下\(a_i\%a_{i-1}\)个。

这两个部分其实是可以分开处理的,重复的部分我们维护\(f_i\)表示第\(i\)次后的序列重复了多少次。然后对于剩下的那一部分也挺好处理的,假设长度为\(c\),那么我们就会一直重复到一个\(a_i\leq c\)的位置才会改变,而每次改变又会变成\(c\%a_i\),所以如果我们二分这个\(a_i\)的话就能做到\(O(\log^2n )\)的了,然后找到\(c<n\)的时候就是让\(1\sim c\)的次数加上一个值,差分就好了。

时间复杂度:\(O(n\log ^2n)\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=1e5+10;
ll n,m,q,a[N],b[N],f[N];
void dfs(ll c,ll d){
	ll x=upper_bound(a+1,a+1+m,c)-a-1;
	if(!x)b[c]+=d;else f[x]+=c/a[x]*d,dfs(c%a[x],d);
	return;
}
signed main()
{
	scanf("%lld%lld",&n,&q);a[++m]=n;
	while(q--){
		ll x;scanf("%lld",&x);
		while(m>0&&x<=a[m])m--;
		a[++m]=x;
	}
	f[m]=1;
	for(ll i=m;i>=2;i--)
		f[i-1]+=a[i]/a[i-1]*f[i],dfs(a[i]%a[i-1],f[i]);
	b[a[1]]+=f[1];
	for(ll i=n;i>=1;i--)b[i]+=b[i+1];
	for(ll i=1;i<=n;i++)printf("%lld\n",b[i]);
	return 0;
}
posted @ 2021-10-25 20:41  QuantAsk  阅读(37)  评论(0编辑  收藏  举报