【agc003E】Sequential operations on Sequence
Description
给你一个数串\(S\),一开始的时候\(S=\{1,2,3,...,n\}\),现在要对其进行\(m\)次操作,每次操作给定一个\(a\),具体方式为将\(S\)重复写很多很多很多次然后取前\(a\)个数作为新的\(S\),问最后得到的\(S\)中\(1\sim n\)每个数的出现次数
Solution
emmm想到了解法但是想用线段树解决原本暴力就可以解决的东西然后就凉了==
首先手玩一下小规模的数据,记当前\(S\)的长度为\(nowlen\),要变成的新的长度为\(x\),\(cnt[i]\)表示当前数字\(i\)出现的次数,分两种情况讨论一下:
1、如果说\(nowlen<=x\):那么所有的\(cnt[i]\)肯定都要\(*x/nowlen\),然后再加上余数的贡献(这部分比较复杂,先不展开说)
2、如果说\(nowlen>x\):那么会发现我们要先找到之前的某次操作使得那次操作之后\(S\)的长度\(<=x\),然后再回到1中的情况
也就是说,我们其实只关心长度不下降的操作,所以接下来提到的操作都是以最后一个操作结尾的不下降操作序列(记为\(st[i]\)好了)中的元素
现在我们着重思考一下那个余数的问题,考虑余数(记为\(rest\))部分的贡献其实就是\(S\)的前\(rest\)个数,那其实我们要做的就是查询某次操作之后\(S\)的前\(rest\)个数中每个数出现的次数,会发现这个其实就是。。原问题,那所以。。递归求解之类的。。?
我们考虑实现一个过程\(solve(x,l)\)表示在第\(x\)次操作之后,\(S\)前\(l\)位中每个数出现的次数(假装返回值是一个数组),记\(cnt[i]\)表示第\(i\)次操作之后\(S\)中每个数字的出现次数(其实就是一个二维数组啦==不过当然只是方便理解这么说,实际实现的时候并不需要开这么一个玩意)那么大概可以写出来一个\(solve(x,l)=l/st[x-1]*cnt[x-1]+solve(x-1,l\%st[x-1])\)
然后接下来就是一个比较重要的经典性质:一个数每次模不大于它的数,总共只能模\(log\)次
(没错反正我当时弱智了没有反应过来==)
所以至多递归\(log\)层(当\(l=0\)的时候自然就直接返回了嘛)
所以我们可以考虑暴力一点来处理这个东西(数据结构什么的是没有前途的qwq比如说一开始想了一个可持久化线段树==显然空间爆炸)
注意到根据上面那条。。递归式,我们可以知道\(cnt[i]\)可以被表示为至多\(log\)个带个系数的\(cnt[j](j<i)\)每次再加上一些不需要递归处理的余数的贡献(也就是当余数\(<=st[1]\)的时候,此时不需要再递归处理而是可以直接得到,因为肯定是对从\(1\)开始的连续的某一段的数字有贡献,或者更加笼统一点就是一个前缀)的和,所以我们考虑维护一个\(addtag\),\(\sum\limits_{i=1}^x addtag[i]\)表示处理到当前,数\(x\)的不需要递归处理的余数贡献,再维护一个\(multag[i]\)表示,用\(cnt[i]\)来表示\(cnt[m]\)(也就是最后的\(cnt\))时,前面需要乘的系数(其实如果再直白一点。。可以说\(addtag\)其实就相当于一个零头,当然这个说法不严谨),那么由于最开始的\(cnt\)是知道的(都是\(1\)),所以最后数字\(x\)的答案(如果不为\(0\)的话)就应该是\(multag[1]*1+\sum\limits_{i=1}^xaddtag[i]\)
最后就是实现问题了,其实就是正常每次把商乘上去就好了好像也没有什么特别要说的。。
mark:(貌似依旧是没有什么建设性的东西。。)把未知的转化成已知的。。?比如说就是把系数推出来然后用最开始的cnt来表示最后的cnt
mark:一个数每次模不大于它的数,总共只能模\(log\)次
代码大概长这个样子
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=1e5+10;
ll a[N];
ll ans[N],st[N],addtag[N],multag[N];
int n,m;
ll nowlen;
void prework(){
st[0]=0; a[0]=n;
for (int i=0;i<=m;++i){
while (st[0]&&st[st[0]]>a[i]) --st[0];
st[++st[0]]=a[i];
}
}
int get_pos(ll x,int r){
int l=1,mid,ret=r;
while (l<=r){
mid=l+r>>1;
if (st[mid]<=x) ret=mid,l=mid+1;
else r=mid-1;
}
return ret;
}
void solve(ll k,ll rest,int ed){
if (!rest) return;
if (rest<=st[1]){
addtag[1]+=k; addtag[rest+1]-=k;
return;
}
int pre=get_pos(rest,ed);
multag[pre]+=k*(rest/st[pre]);
solve(k,rest%st[pre],pre-1);
}
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
ll rest,tmp,x;
scanf("%d%d",&n,&m);
nowlen=n;
for (int i=1;i<=m;++i) scanf("%lld",a+i);
prework();
multag[st[0]]=1;
for (int i=st[0];i>=2;--i){
tmp=st[i]/st[i-1];
rest=st[i]%st[i-1];
multag[i-1]+=multag[i]*tmp;
solve(multag[i],rest,i-1);
}
for (int i=1;i<=st[1];++i){
addtag[i]+=addtag[i-1];
printf("%lld\n",addtag[i]+multag[1]);
}
for (int i=st[1]+1;i<=n;++i) printf("0\n");
}