题意:给出一个长度为n的环,一个常数k和q次询问,每次跳跃会从第i个点跳到第(i+k) mod n + 1 个点(每次都是从第一个点开始跳)。每一个点都有一个权值,记为ai。每次询问给出一个m,求m次跳跃的权值和对1e9+7取模。
分析:显然可以考虑循环节。考虑用倍增O(nlogm)预处理出从每个点开始跳2的幂次之后能获得的点权和,直接O(logm)查询
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long//空间紧会mle
// #define int __int128
#define pii pair<int,int>
#define endl '\n'//交互题需要endl刷新
const int mod=1e9+7;
const int maxn=1e5+5;
int a[maxn];
int go[maxn][65];
int sum[maxn][65];
void solve()
{
int n,k,q;
cin>>n>>k>>q;
for(int i=1;i<=n;i++)
{
cin>>a[i];
go[i][0]=(i+k)%n+1;
sum[i][0]=a[i];
}
for(int j=1;j<=60;j++)//环
{
for(int i=1;i<=n;i++)
{
go[i][j]=go[go[i][j-1]][j-1];
sum[i][j]=(sum[i][j-1]+sum[go[i][j-1]][j-1])%mod;
}
}
for(int i=1;i<=q;i++)
{
int m;
cin>>m;
int ans=0;
int pos=1;
for(int i=0;i<=60;i++)
{
if((m>>i)&1)
{
ans=(ans+sum[pos][i])%mod;
pos=go[pos][i];
}
}
if(i!=q) printf("%d ",ans);
else printf("%d\n",ans);
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t=1;
// cin>>t;cin.ignore();//在getline之前使用
while(t--) solve();
}
倍增板子题:
void solve()
{
stack<int>s;
int n,q;
cin>>n>>q;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
for(int i=n;i>=1;i--)
{
while(!s.empty()&&a[s.top()]<=a[i]) s.pop();
r[i]=s.empty()?n+1:s.top();
if(r[i]==i+1)
{
go[i][0]=i+1;
sum[i][0]=0;
}
else
{
go[i][0]=r[i];
sum[i][0]=1;
}
s.push(i);
}
for(int i=0;i<=20;i++) go[n+1][i]=n+1;
for(int j=1;j<=20;j++)
{
for(int i=1;i<=n;i++)
{
go[i][j]=go[go[i][j-1]][j-1];
sum[i][j]=sum[i][j-1]+sum[go[i][j-1]][j-1];
}
}
while(q--)
{
int l,r;
cin>>l>>r;
int ans=0;
for(int i=20;i>=0;i--)
{
if(go[l][i]<=r) ans+=sum[l][i],l=go[l][i];
}
if(l!=r) ans++;
cout<<ans<<endl;
}
}