AGC003 E

题意:
有一个长度为n的序列,初始为1~n。m个操作,每个操作ai表示把当前序列复制无限次,然后取前ai个数作为新序列。问最终序列里1~n各出现多少次。
n,m<=10^5
ai<=10^18

#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<iostream>
#define N 110000
#define LL long long
using namespace std;
LL sta[N],a[N],t[N],s[N],ans;
int tp,n,m;
int td(LL k,int r)
{
    int l=1,res=0;
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(a[mid]<=k) res=mid,l=mid+1;
        else r=mid-1;
    }
    return res;
}
int main()
{
    scanf("%d%d",&n,&m);
    sta[tp=1]=n;
    for(int i=1;i<=m;i++)
    {
        LL x;scanf("%lld",&x);
        while(x<=sta[tp]) tp--;
        sta[++tp]=x;
    }
    m=tp;
    for(int i=1;i<=m;i++) a[i]=sta[i];
    t[m]=1;
    for(int i=m;i>=1;i--)
    {
        LL k=a[i];int p=td(k,i-1);
        while(p)
        {
            t[p]+=(k/a[p])*t[i];
            k%=a[p];
            p=td(k,p-1);
        }
        s[1]+=t[i];s[k+1]-=t[i];
    }
    for(int i=1;i<=n;i++) {ans+=s[i];printf("%lld\n",ans);}
    return 0;
}

题解:
感觉做法很不显然,真不知道出题人怎么想的。。
最开始先取一次n,方便处理。
发现如果 ai>=ai+1 那么ai是无效的,把序列变成严格上升的。
然后令t[i]为第i次操作后的序列在最终序列中出现多少次,有t[m]=1。
i从大到小枚举
然后考虑对于t[i],把他等价的转移到某些t[j]上(i>j)
维护一个k表示当前还有多长的后缀没有转移,由于每次转移拿掉的都是周期,所以这个k是个border
无视就好,和解法没有关系
循环这个过程:
1、找到最大的j,使 aj<=k ,那么当前的后缀k就是第j个序列循环得到的
2、 t[j]+=kajt[i] ,然后k对 aj 取模
最后剩下的k是小于 a1 同时也小于n的,就是代表最初的k个数,直接处理答案就好。
用心感受一下,就能体会到正确性了。。
k每次取模至少会变小一半,用二分找j的位置就是 O(mlog(m)log(am)) 的了。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值