云计算百科
云计算领域专业知识百科平台

ABC 杂题选讲3

题目 on Luogu:[ABC438E] Heavy Buckets

前置芝士:倍增。

如果你直接去看这道题目,那么肯定会没有思路。

一共要进行

10

9

10^9

109 次操作,又要询问一些操作后任意一个水桶的水容量,这看起来根本不可实现。这时,我们可以使用一种非常神奇的思想——倍增。如下是两个状态的定义:

  • n

    x

    t

    j

    ,

    i

    nxt_{j,i}

    nxtj,i 表示从第

    j

    j

    j 个水桶进行了

    2

    i

    2^i

    2i 次操作后所在的人的编号;

  • s

    u

    m

    j

    ,

    i

    sum_{j,i}

    sumj,i 表示第

    j

    j

    j 个水桶进行了

    2

    i

    2^i

    2i 次操作后水桶中的总水量。

显而易见,这两个数组的预处理非常容易:

n

x

t

j

,

i

=

n

x

t

n

x

t

j

,

i

1

,

i

1

nxt_{j,i}=nxt_{nxt_{j,i-1},i-1}

nxtj,i=nxtnxtj,i1,i1

s

u

m

j

,

i

=

s

u

m

j

,

i

1

+

s

u

m

n

x

t

j

,

i

1

i

1

sum_{j,i}=sum_{j,i-1}+sum_{nxt_{j,i-1}i-1}

sumj,i=sumj,i1+sumnxtj,i1i1

对于初始化,

n

x

t

i

,

0

=

a

i

nxt_{i,0}=a_i

nxti,0=ai 即进行

2

0

=

1

2^0=1

20=1 次传递自然在第

a

i

a_i

ai 个人手里;

s

u

m

i

,

0

=

i

sum_{i,0}=i

sumi,0=i 即经过一次操作后第

i

i

i 个水桶只被第

i

i

i 个人加过水。

接下来要查询第

T

T

T 次操作以后第

B

B

B 个水桶的水容量,这时可以直接进行二进制跳法,从

log

2

(

10

9

)

\\log_2(10^9)

log2(109) 开始,依次进行尝试,如果可以跳,则向前跳,并加上这一次跳的水容量,并将

T

T

T 减少相应的值。最终结果即为跳过程中累计的水量和。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+5;
int a[N],nxt[N][33],sum[N][33];//记得开大一点
int query(int x,int y){
int ans=0;
for(int i=31;i>=0;i){//从大到小跳
if(x>=(1ll<<i)){//可以跳
ans+=sum[y][i];//累计水量
y=nxt[y][i];//向前跳
x-=(1<<i);//减去这次跳的操作数
}
}
return ans;
}
void solve(){
int n,q;
cin>>n>>q;
for(int i=1;i<=n;i++){
cin>>a[i];
nxt[i][0]=a[i];//初始化
sum[i][0]=i;
}
for(int i=1;i<=31;i++){//预处理
for(int j=1;j<=n;j++){
nxt[j][i]=nxt[nxt[j][i1]][i1];
sum[j][i]=sum[j][i1]+sum[nxt[j][i1]][i1];
}
}
while(q){
int t,b;
cin>>t>>b;
cout<<query(t,b)<<'\\n';//查询
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T=1;
while(T) solve();
return 0;
}

赞(0)
未经允许不得转载:网硕互联帮助中心 » ABC 杂题选讲3
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!