1. 程式人生 > 其它 >AT2005-[AGC003E]Sequential operations on Sequence【差分,思維】

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;
}