题目链接
解题思路
先考虑对于 \(1 \sim n\) 中的每一个点往后跳 \(1\) 次会跳的距离。
那么为什么只用处理 \(1 \sim n\) 这些点而不去处理其余的点往后跳的距离呢?
我们可以发现,由于字符串是无线循环的,所以对于位置模 \(n\) 的结果相同时,那么往后跳的距离也是相同的。
我们可以先将字符串断环成链倍长后做前缀和来处理 \(l \sim r\) 中的 \(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;
}
}
标签:nxt,return,ll,往后,x%,杂题,sum,P11267
From: https://www.cnblogs.com/wangmarui/p/18536681