您的位置:首页 > 产品设计 > UI/UE

[agc003e]Sequential operations on Sequence

2018-02-27 15:12 357 查看

题目大意

有一个数字串S,初始长度为n,是1 2 3 4 …… n。

有m次操作,每次操作给你一个正整数a[i],你先把S无穷重复,然后把前a[i]截取出来成为新的S。

求m次操作后,每个数字在S中出现的次数。

n,m<=10^5,a[i]<=10^18。

解题思路

一般这种题考虑倒着退回去。

考虑一个函数solve(x,l),它返回的是一个cnt[1..n]表示每个数字在第x次操作后的序列的前l位中出现的次数。

发现solve(x,l)=(l/a[x-1])*solve(x-1,a[x-1])+solve(x-1,l%a[x-1])。

一种可行的做法是直接暴力算这个东西,递归到x=0的时候,就可以直接返回值。

考虑优化。首先可以发现,假如我们先不管前面那部分,可以发现l出现变化的次数不会超过log次。你至少每次减小一半。也就是说很多时候你后面那部分直接solve(x-1,l)。你只要快速地从x往前找到第一个a[x]<=l的即可。

再考虑前面那部分,这部分很显然,你不需要真正递归进去,只需要记录下每个solve(x,a[x])前面的系数,然后倒着扫即可。设系数数组xs[i]表示到目前为止,solve(i,a[i])的系数。xs
=1.

也就是说,一开始答案ans=xs
*solve(i,a[i]),然后你把它表示成log个xs[j]*solve(j,a[j])的和,然后你再把最大的那个solve继续表示成更小的solve相加。一直到最后,你会弄到x=0,利用xs[0]和并差分数组维护一下solve(0,y)(其中y!=n),就可以得出答案了。

代码

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;
typedef double db;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
#define cmax(a,b) (a=(a>b)?a:b)
#define cmin(a,b) (a=(a<b)?a:b)
const int N=2e5+5,mo=1e9+7,rt=3;
ll Log
,le,ri,l,r,i,j,n,m,mid,kan;
ll len,f[20]
,a
,xs
,inc
,prt
,sum;
void rmq()
{
fo(i,0,m) f[0][i]=a[i];
fo(i,1,m+1) Log[i]=trunc(log(i)/log(2));
fo(j,1,Log[m+1])
fo(i,0,m-(1<<j)+1)
f[j][i]=min(f[j-1][i],f[j-1][i+(1<<j-1)]);
}
ll getmn(ll x,ll y)
{
ll z=Log[y-x+1];
return min(f[z][x],f[z][y-(1<<z)+1]);
}
ll find(ll v)
{
le=0;
ri=r;
while (le<ri)
{
mid=(le+ri+1)/2;
if (getmn(mid,r)<=v) le=mid;
else ri=mid-1;
}
return le;
}
int main()
{
freopen("t5.in","r",stdin);
freopen("t5.out","w",stdout);
scanf("%lld %lld",&n,&m);
a[0]=n;
fo(i,1,m)
scanf("%lld",a+i);
rmq();
xs[m]=1;
fd(i,m,1)
{
r=i-1;
len=a[i];
while (r>=0)
{
l=find(len);// pos<=r && a[pos]<=len
xs[l]+=xs[i]*(len/a[l]);
len%=a[l];
r=l-1;
}
inc[len]+=xs[i];
}
fd(i,n,1)
{
sum+=inc[i];
prt[i]=sum+xs[0];
}
fo(i,1,n) printf("%lld\n",prt[i]);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: