正题
题目链接:https://www.luogu.com.cn/problem/AT2005
题目大意
开始有一个1∼n1\sim n1∼n依次排列的序列,然后QQQ次,第iii次把序列长度变为aia_iai,不足的从前往后循环填充。
求最后每个数字的出现次数。
1≤n,q≤105,1≤ai≤10181\leq n,q\leq 10^5,1\leq a_i\leq 10^{18}1≤n,q≤105,1≤ai≤1018
解题思路
首先肯定是先搞出一个单调栈来,然后考虑每次复制重复的部分。
考虑第iii次,首先是原先的序列重复⌊aiai−1⌋\lfloor\frac{a_i}{a_{i-1}}\rfloor⌊ai−1ai⌋次,然后后面会剩下ai%ai−1a_i\%a_{i-1}ai%ai−1个。
这两个部分其实是可以分开处理的,重复的部分我们维护fif_ifi表示第iii次后的序列重复了多少次。然后对于剩下的那一部分也挺好处理的,假设长度为ccc,那么我们就会一直重复到一个ai≤ca_i\leq cai≤c的位置才会改变,而每次改变又会变成c%aic\%a_ic%ai,所以如果我们二分这个aia_iai的话就能做到O(log2n)O(\log^2n )O(log2n)的了,然后找到c<nc<nc<n的时候就是让1∼c1\sim c1∼c的次数加上一个值,差分就好了。
时间复杂度:O(nlog2n)O(n\log ^2n)O(nlog2n)
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;
}