题解:P11234 [CSP-S 2024] 擂台游戏(暂无数据)
Lonely_NewYear · · 题解
P11234 题解
考场做法。
题目大意
太复杂,自己看题面吧。
题目分析
直线考虑怎么线性。
先把整个过程看成一个完全二叉树。
一个人能走到最后有两个条件:自己擂主时能力值要够大,别人擂主时别人的能力值要够小。第一个条件很容易,预处理出每个点到根最后一次当擂主的轮数就行。第二个条件则需要预处理出每个子树最小能让能力值多小的人胜出。
实际上每个子树只有两种可能:胜出者固定为某个人,或者胜出者的能力值可以取当前轮数以上的任意值。记
容易发现在
那么当一个节点的
求出叶子节点的
整个过程是这样的:初始
人数太少时树的结构会变化,只需要每次人数达到
总复杂度线性。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100001;
int n,m,k;
int a[N];
int c[N];
char d[N*2];
int f[N*4],g[N*4],h[N*4],b[N*4];
void dfs(int i,int t){
f[i]=-1,g[i]=t+1;
if(i>=(1<<k))return;
int j=(i<<1)+d[i]-'0';
h[j]=h[j^1]=h[i]-1;
if(b[i])b[j]=b[j^1]=b[i];
else b[j]=h[i],b[j^1]=0;
dfs(j,t),dfs(j^1,t);
}
void upd(int i,int t){
if(!i)return;
if(f[i]!=-1)return;
int j=(i<<1)+d[i]-'0';
if(f[j]>=h[i])f[i]=f[j],g[j^1]=min(g[j^1],t);
else if(f[j]!=-1)f[i]=f[j^1];
if(f[j]!=-1)upd(i>>1,t);
}
void get(int i){
if(i>=(1<<k))return;
int j=(i<<1)+d[i]-'0';
g[j]=min(g[j],g[i]);
g[j^1]=min(g[j^1],g[i]);
get(j),get(j^1);
}
ll s[N*2],res[N*2];
void solve(int t){
int p=1<<(k-t);
h[p]=t,b[p]=0;
dfs(p,1<<t);
for(int i=1;i<=n;i++){
int j=i+(1<<k)-1;
if(a[i]<b[j])g[j]=min(g[j],i);
f[j]=a[i];
upd(j>>1,i);
}
get(p);
for(int i=1;i<=(1<<t);i++)s[i]=0;
for(int i=1;i<=(1<<t);i++){
int j=i+(1<<k)-1;
s[g[j]-1]+=i;
}
for(int i=(1<<t);i>=1;i--){
s[i-1]+=s[i];
res[i]=s[i];
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
while((1<<k)<n)k++;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=m;i++)cin>>c[i];
for(int i=k-1;i>=1;i--)
for(int j=0;j<(1<<i);j++)
cin>>d[(1<<i)+j];
cin>>d[1];
int T;
cin>>T;
while(T--){
int x[4];
cin>>x[0]>>x[1]>>x[2]>>x[3];
for(int i=1;i<=n;i++)a[i]^=x[i%4];
for(int i=k;i>=0;i--)solve(i);
ll ans=0;
for(int i=1;i<=m;i++)ans^=i*res[c[i]];
cout<<ans<<'\n';
for(int i=1;i<=n;i++)a[i]^=x[i%4];
}
return 0;
}
谢谢观看!