「杂题乱刷2」P11267

题目链接

P11267 【MX-S5-T1】王国边缘

解题思路

先考虑对于 1n 中的每一个点往后跳 1 次会跳的距离。

那么为什么只用处理 1n 这些点而不去处理其余的点往后跳的距离呢?

我们可以发现,由于字符串是无线循环的,所以对于位置模 n 的结果相同时,那么往后跳的距离也是相同的。

我们可以先将字符串断环成链倍长后做前缀和来处理 lr 中的 1 的数量。

然后容易二分得出往后跳的距离。

然后我们就可以求出对于每个点 i 往后可以跳跃的距离。

然后问题询问的是往后跳 k 次的最终位置。

这是一个经典的倍增问题,直接倍增即可。

注意,在倍增过程中,可能答案会爆 long long

参考代码

记得加上快读。

ll n,m,k;
ll x,y;
string s;
ll corn=1;
ll mod=1e9+7;
ll sum[400010];
__int128 nxt[400010][70];
ll ask(ll x) // 1~x 有几个 1
{
    return sum[x%n]+sum[n]*(x/n);
}
ll query(ll l,ll r){// l~r 中有几个 1
    return ask(r)-ask(l-1);
}
ll f(__int128 x)
{
    if(x%n==0)
        return n;
    return x%n;
}
ll corn2=1;
void solve()
{
    _clear();
    cin>>n>>m>>k>>s;
    s=s+s;
    s=' '+s;
    forl(i,1,n)
        corn&=s[i]=='0',
        corn2&=s[i]=='1';
    if(corn)
    {
        while(k--)
            cin>>x>>y,
            cout<<(x+y)%mod<<endl;
        return ;
    }
    if(corn2)
    {
        while(k--)
            cin>>x>>y,
            cout<<(x+(y%mod)*(m%mod))%mod<<endl;
        return ;
    }
    forl(i,1,n*2)
        sum[i]=sum[i-1]+(s[i]=='1');
    forl(i,1,n)
    {
        ll L=i+1,R=i+m;
        while(L<R)
        {
            ll Mid=(L+R)/2;
            if(query(Mid+1,i+m)>=1)
                L=Mid+1;
            else
                R=Mid;
        }
        if(query(L,L)>=1)
            nxt[i][0]=L-i;
        else
            nxt[i][0]=1;
    }
    forl(j,1,62)
        forl(i,1,n)
            nxt[i][j]=(nxt[i][j-1]+nxt[f(i+nxt[i][j-1])][j-1]);
    while(k--)
    {
        cin>>x>>y;
        ll ans=x%mod;
        forr(i,62,0)
            if(y&(1ll<<i))
            {
                x%=n;
                if(x==0)
                    x=n;
                ans=(ans+nxt[x][i]%mod)%mod;
                x+=nxt[x][i]%n;
            }
        cout<<ans<<endl;
    }
}
posted @   wangmarui  阅读(27)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示