[CSP-S 2024] 擂台游戏 题解
[CSP-S 2024] 擂台游戏 题解
知识点
模拟,贪心,递归,差分。
题意简述
给你 \(n\) 个选手及其能力值 \(\{a_i\}\) 和 \(Q\) 个询问,每个询问给定一个 \(c_i\),问:只取前 \(c_i\) 个人进行淘汰赛时,可能获胜的人的编号和(人数不足时,自动补齐至最小的大于等于 \(c_i\) 的 \(2^k,k \in \mathbb{N}\))。对于每一轮会给出擂主,擂主对战胜利条件是:能力值大于当前轮数。
分析
我们考虑直接把所有对战人数的答案都直接处理出来,分成不同的最大层数,这样就可以建出 \(\lceil \log_2{n} \rceil\) 棵满二叉树来处理。
发现在每一棵满二叉树上每个点它能够获胜的人数值会连成一个区间,并且同一棵满二叉树的所有合法的区间起点相同,那么就可以用差分优化。
预处理固定情况
我们知道当树内的一个区间能力值都是固定的时,那么这里的胜者也固定,包括从有多少人时开始固定也可以求出。
设满二叉树上点 \(u\) 的胜者能力值为 \(wn_u\),开始固定时人数为 \(wt_u\),分类讨论:
边界情况:满二叉树上点 \(u\) 仅代表一个点的时候,那么 \(wn_u\) 为该点的能力值,\(wt_u\) 就为该点的编号。
设现在在第 \(h(h\in[1,\lceil \log_2{n} \rceil])\) 轮,左儿子为 \(ls\),右儿子为 \(rs\)。
- 擂主在左儿子区间
- 擂主会输,即 \(wn_{ls}<h\),则 \(wn_u = wn_{rs},wt_u = wt_{rs}\);
- 擂主会赢,即 \(wn_{ls} \ge h\),则 \(wn_u = wn_{ls},wt_u = wt_{ls}\);
- 擂主在右儿子区间
- 擂主会输,即 \(wn_{rs}<h\),则 \(wn_u = wn_{ls},wt_u = wt_{rs}\);
- 擂主会赢,即 \(wn_{rs} \ge h\),则 \(wn_u = wn_{rs},wt_u = wt_{rs}\)。
一遍递归遍历即可,形参记录点编号 \(u\) 和高度 \(h\)。
#define v (u<<1)
void dfs0(int u,int h) {
if(!h)return wn[u]=a[wt[u]=u-(1<<k)+1],void();
dfs0(v,h-1),dfs0(v|1,h-1),wt[u]=wt[v|d[u]|(wn[v]<h)],wn[u]=wn[v|(d[u]^(wn[v|d[u]]<h))];
}
#undef v
处理每一棵满二叉树
这里对于每一棵满二叉树分别处理,主要是因为它们顶层不同,同时方便处理合法的区间起点从而进行差分。
设现在在第 \(h(h\in[1,\lceil \log_2{n} \rceil])\) 轮,擂主所在的子节点为 \(son\),另一个则为 \(son'\),记录参数 \(L,R\) 表示最后更新差分数组的区间。
边界情况:满二叉树上点 \(u\) 仅代表一个点的时候,我们更新差分数组,不过要把该点是否固定的情况给分开,所以要记个它祖先作为擂主时的最大轮数值(高度) \(h_{mx}\)(没有记为 \(0\))。
- 到 \(son\) 时,只需更新 \(h_{mx}\) 即可,然后往下递归。
- 到 \(son'\) 时则需分类讨论修改 \(R\):
- 擂主会输,即 \(wn_{son} < h\):不做更改。
- 擂主会赢,即 \(wn_{son} \ge h\):更新 \(R \gets \min{(R,wt_{son})}\);
依旧是一遍递归,形参记录点编号 \(u\) 、祖先作为擂主时的最大轮数值(高度) \(h_{mx}\)、高度 \(h\) 和 \(L,R\) 表示最后更新差分数组的区间。
#define v (u<<1)
void dfs1(int u,int hmx,int h,int L,int R) {
if(L>R)return;
if(!h) {
int x(u-(1<<k)+1);
if(L<min(R,x))sum[L]+=x,sum[min(R,x)]-=x;
if(a[x]>=hmx&&max(L,x)<R)sum[max(L,x)]+=x,sum[R]-=x;
return;
}
dfs1(v|d[u],hmx?hmx:h,h-1,L,R),dfs1(v|!d[u],hmx,h-1,L,wn[v|d[u]]>=h?min(wt[v|d[u]],R):R);
}
#undef v
处理差分和询问
最后的工作就比较简单。
signed main() {
rd(n),rd(m);
while((1<<k)<n)++k;
FOR(i,1,n)rd(a_[i]);
FOR(i,1,m)rd(c[i]);
DOR(i,k-1,0) {
scanf("%s",s);
DOR(j,(1<<i)-1,0)d[j|1<<i]=s[j]^48;
}
rd(Q);
while(Q--) {
static int X[4];
rd(X[0]),rd(X[1]),rd(X[2]),rd(X[3]);
FOR(i,1,n)a[i]=a_[i]^X[i&3];
dfs0(1,k),RCL(sum+1,0,ll,n);
FOR(i,0,k)dfs1(1<<(k-i),0,i,!i?1:(1<<(i-1))+1,(1<<i)+1);
FOR(i,1,n)sum[i]+=sum[i-1];
ll ans(0);
FOR(i,1,m)ans^=i*sum[c[i]];
wr(ans);
}
return 0;
}
代码
时间复杂度:\(O(Tn)\),空间复杂度:\(O(2^{\lceil \log_2{n}\rceil})\)。
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define RCL(a,b,c,d) memset(a,b,sizeof(c)*(d))
#define FOR(i,a,b) for(int i(a);i<=(int)(b);++i)
#define DOR(i,a,b) for(int i(a);i>=(int)(b);--i)
using namespace std;
constexpr int N((1<<17)+10);
namespace IOstream {
#define getc() getchar()
#define putc(ch) putchar(ch)
#define isdigit(ch) ('0'<=(ch)&&(ch)<='9')
template<class T>void rd(T &x) {
static char ch(0);
for(x=0,ch=getc(); !isdigit(ch); ch=getc());
for(; isdigit(ch); x=(x<<1)+(x<<3)+(ch^'0'),ch=getc());
}
template<class T>void wr(T x,const char End='\n') {
static int top(0);
static int st[50];
do st[++top]=x%10,x/=10;
while(x);
while(top)putc(st[top]^'0'),--top;
putc(End);
}
} using namespace IOstream;
bool d[N];
char s[N];
int n,m,k,Q;
int a_[N],a[N],c[N],wt[N<<1],wn[N<<1];
ll sum[N];
#define v (u<<1)
void dfs0(int u,int h) {
if(!h)return wn[u]=a[wt[u]=u-(1<<k)+1],void();
dfs0(v,h-1),dfs0(v|1,h-1),wt[u]=wt[v|d[u]|(wn[v]<h)],wn[u]=wn[v|(d[u]^(wn[v|d[u]]<h))];
}
void dfs1(int u,int hmx,int h,int L,int R) {
if(L>R)return;
if(!h) {
int x(u-(1<<k)+1);
if(L<min(R,x))sum[L]+=x,sum[min(R,x)]-=x;
if(a[x]>=hmx&&max(L,x)<R)sum[max(L,x)]+=x,sum[R]-=x;
return;
}
dfs1(v|d[u],hmx?hmx:h,h-1,L,R),dfs1(v|!d[u],hmx,h-1,L,wn[v|d[u]]>=h?min(wt[v|d[u]],R):R);
}
#undef v
signed main() {
rd(n),rd(m);
while((1<<k)<n)++k;
FOR(i,1,n)rd(a_[i]);
FOR(i,1,m)rd(c[i]);
DOR(i,k-1,0) {
scanf("%s",s);
DOR(j,(1<<i)-1,0)d[j|1<<i]=s[j]^48;
}
rd(Q);
while(Q--) {
static int X[4];
rd(X[0]),rd(X[1]),rd(X[2]),rd(X[3]);
FOR(i,1,n)a[i]=a_[i]^X[i&3];
dfs0(1,k),RCL(sum+1,0,ll,n);
FOR(i,0,k)dfs1(1<<(k-i),0,i,!i?1:(1<<(i-1))+1,(1<<i)+1);
FOR(i,1,n)sum[i]+=sum[i-1];
ll ans(0);
FOR(i,1,m)ans^=i*sum[c[i]];
wr(ans);
}
return 0;
}