[CSP-S 2024] 擂台游戏满分代码

# [CSP-S 2024] 擂台游戏

## 题目描述

小 S 想要举办一场擂台游戏,如果共有 $2^k$ 名选手参加,那么游戏分为 $k$ 轮进行:

- 第一轮编号为 $1, 2$ 的选手进行一次对局,编号为 $3, 4$ 的选手进行一次对局,以此类推,编号为 $2^k - 1, 2^k$ 的选手进行一次对局。
- 第二轮在只保留第一轮的胜者的前提下,相邻的两位依次进行一场对局。
- 以此类推,第 $k - 1$ 轮在只保留第 $k - 2$ 轮的 $4$ 位胜者的前提下,前两位、后两位分别进行对局,也就是所谓的半决赛。
- 第 $k$ 轮即为半决赛两位胜者的决赛。

确定了游戏晋级的规则后,小 S 将比赛的规则设置为了擂台赛。具体而言,每位选手都有一个能力值 $a_1, a_2, \dots , a_{2^k}$,能力值为 $[0,2^{31}-1]$ 之内的整数。对于每场比赛,会先抽签决定一个数 $0/1$,我们将第 $R$ 轮的第 $G$ 场比赛抽到的数记为 $d_{R,G}$。抽到 $0$ 则表示表示编号小的选手为擂主,抽到 $1$ 则表示编号大的选手为擂主。擂主获胜当且仅当他的能力值 $a\geq R$。也就是说,游戏的胜负只取决于**擂主的能力值**与**当前比赛是第几轮**的大小关系,**与另一位的能力值无关**。

现在,小 S 先后陆续收到了 $n$ 位选手的报名信息,他们分别告知了小 S 自己的能力值。小 S 会按照报名的先后顺序对选手进行编号为 $1, 2, \dots, n$。小 S 关心的是,补充**尽量少**的选手使总人数为 $2$ 的整次幂,且所有选手进行一次完整的擂台游戏后,所有可能成为总冠军的选手的**编号之和**是多少。

形式化地,设 $k$ 是最小的非负整数使得 $2^k\geq n$,那么应当补充 $(2^k-n)$ 名选手,且补充的选手的能力值可以任取 $[0,2^{31}-1]$ 之内的整数。**如果补充的选手有可能取胜,也应当计入答案中**。

当然小 S 觉得这个问题还是太简单了,所以他给了你 $m$ 个询问 $c_1,c_2,\dots,c_m$。小 S 希望你帮忙对于每个 $c_i$ 求出,在只收到前 $c_i$ 位选手的报名信息时,这个问题的答案是多少。

## 输入格式

**本题的测试点包含有多组测试数据。** 但不同测试数据只是通过修改 $a_1, a_2, \dots , a_n$ 得到,其他内容均保持不变,请参考以下格式。其中 $\oplus$ 代表异或运算符,$a \bmod b$ 代表 $a$ 除以 $b$ 的余数。

输入的第一行包含两个正整数 $n, m$,表示报名的选手数量和询问的数量。

输入的第二行包含 $n$ 个非负整数 $a'_1,a'_2,\dots,a'_n$,这列数将用来计算真正的能力值。

输入的第三行包含 $m$ 个正整数 $c_1, c_2, \dots , c_m$,表示询问。

设 $K$ 是使得 $2^K \geq n$ 的最小的非负整数,接下来的 $K$ 行当中,第 $R$ 行包含 $2^{K-R}$ 个数(无空格),其中第 $G$ 个数表示第 $R$ 轮的第 $G$ 场比赛抽签得到的 $d_{R,G}=0/1$。

注意,由于询问只是将人数凑齐到 $2^k\geq c_i$,这里的 $k\leq K$,因此你未必会用到全部的输入值。

接下来一行包含一个正整数 $T$,表示有 $T$ 组测试数据。

接下来共 $T$ 行,每行描述一组数据,包含 $4$ 个非负整数 $X_0,X_1,X_2,X_3$,该组数据的能力值 $a_i=a'_i \oplus X_{i\bmod 4}$,其中 $1\leq i\leq n$。

## 输出格式

共输出 $T$ 行,对于每组数据,设 $A_i$ 为第 $i$($1 \leq i \leq m$)组询问的答案,你只需要输出一行包含一个整数,表示 $(1\times A_1) \oplus (2\times A_2) \oplus \dots \oplus (m\times A_m)$ 的结果。

## 样例 #1

### 样例输入 #1

```
5 5
0 0 0 0 0
5 4 1 2 3
1001
10
1
4
2 1 0 0
1 2 1 0
0 2 3 1
2 2 0 1
```

### 样例输出 #1

```
5
19
7
1
```

## 提示

**【样例 1 解释】**

共有 $T = 4$ 组数据,这里只解释第一组。$5$ 名选手的真实能力值为 $[1, 0, 0, 2, 1]$。$5$ 组询问分别是对长度为 $5, 4, 1, 2, 3$ 的前缀进行的。

1. 对于长度为 $1$ 的前缀,由于只有 $1$ 号一个人,因此答案为 $1$。
2. 对于长度为 $2$ 的前缀,由于 $2$ 个人已经是 $2$ 的幂次,因此不需要进行扩充。根据抽签 $d_{1,1} = 1$ 可知 $2$ 号为擂主,由于 $a_2 < 1$,因此 $1$ 号获胜,答案为 $1$。
3. 对于长度为 $3$ 的前缀,首先 $1$ 号、$2$ 号比赛是 $1$ 号获胜(因为 $d_{1,1} = 1$,故 $2$ 号为擂主,$a_2 < 1$),然后虽然 $4$ 号能力值还不知道,但 $3$ 号、$4$ 号比赛一定是 $4$ 号获胜(因为 $d_{1,2} = 0$,故 $3$ 号为擂主,$a_3 < 1$),而决赛 $1$ 号、$4$ 号谁获胜都有可能(因为 $d_{2,1} = 1$,故 $4$ 号为擂主,如果 $a_4 < 2$ 则 $1$ 号获胜,$a_4 \geq 2$ 则 $4$ 号获胜)。综上所述,答案为 $1 + 4 = 5$。
4. 对于长度为 $4$ 的前缀,我们根据上一条的分析得知,由于 $a_4 \geq 2$ ,所以决赛获胜的是 $4$ 号。
5. 对于长度为 $5$ 的前缀,可以证明,可能获胜的选手包括 $4$ 号、$7$ 号、$8$ 号,答案为 $19$。

因此,该组测试数据的答案为 $(1 \times 19) \oplus (2 \times 4) \oplus (3 \times 1) \oplus (4 \times 1) \oplus (5 \times 5) = 5$。

**【样例 2】**

见选手目录下的 arena/arena2.in 与 arena/arena2.ans。

这组样例满足特殊性质 A。

**【样例 3】**

见选手目录下的 arena/arena3.in 与 arena/arena3.ans。

这组样例满足特殊性质 B。

**【样例 4】**

见选手目录下的 arena/arena4.in 与 arena/arena4.ans。

**【样例 5】**

见选手目录下的 arena/arena5.in 与 arena/arena5.ans。

**【数据范围】**

对于所有测试数据,保证:$2 \leq n, m \leq 10^5$,$0 \leq a_i, X_j < 2^{31}$,$1 \leq c_i \leq n$,$1 \leq T \leq 256$。

| 测试点 | $T=$ | $n,m\leq$ | 特殊性质 A | 特殊性质 B |
| :----------: | :----------: | :----------: | :----------: | :----------: |
| $1\sim 3$ | $1$ | $8$ | 否 | 否 |
| $4,5$ | $1$ | $500$ | 是 | 否 |
| $6\sim 8$ | $1$ | $500$ | 否 | 是 |
| $9,10$ | $1$ | $5000$ | 否 | 否 |
| $11,12$ | $1$ | $10^5$ | 是 | 否 |
| $13\sim 15$ | $1$ | $10^5$ | 否 | 是 |
| $16,17$ | $4$ | $10^5$ | 否 | 否 |
| $18,19$ | $16$ | $10^5$ | 否 | 否 |
| $20,21$ | $64$ | $10^5$ | 否 | 否 |
| $22,23$ | $128$ | $10^5$ | 否 | 否 |
| $24,25$ | $256$ | $10^5$ | 否 | 否 |


特殊性质 A:保证询问的 $c_i$ 均为 $2$ 的幂次。

特殊性质 B:保证所有的 $d_{R,G} = 0$。

#include <cstdio>
#include <algorithm>
using namespace std; 
inline int read(){
	char c = getchar();
	while(c<48||c>57) c=getchar();
	int x = 0;
	while(48<=c&&c<=57){
		x = (x<<3)+(x<<1)+c-48;
		c = getchar();
	}
	return x;
}
typedef long long int64;
// 深度 x+1 的前缀,dpref(x)+i 为深度 x 中排行 i 的结点编号 
#define dpref(x) ((1<<(x))-1)
// 深度 x+1 的结点数 
#define dsize(x) (1<<(x))
#define fa(x) ((x)>>1)
#define ls(x) ((x)<<1)
#define rs(x) ((x)<<1|1)
#define bro(x) ((x)^1)
// x 是左(0)/右(1)子树 
#define lr(x) ((x)&1)

int n, m, a[100001], c[100001];
int K, d[131072];
int T, X[4]; 

void init();
void solve();

int main(){
	n = read(); m = read();
	for(K=1; (1<<K)<n; ++K);
	for(int i=1; i<=n; ++i) a[i] = read();
	for(int i=1; i<=m; ++i){
		c[i] = read();
	}
	char buffer[65537];
	for(int R=1; R<=K; ++R){
		scanf("%s", buffer);
		for(int G=1; G<=dsize(K-R); ++G){
			d[dpref(K-R)+G] = buffer[G-1]=='1';
		}
	}
	
	init();
	
	T = read();
	while(T--){
		for(int i=0; i<4; ++i) X[i] = read();
		for(int i=1; i<=n; ++i) a[i] ^= X[i%4];
		solve();
		// 技巧: 异或运算的自逆性 (a^b)^b = a 
		for(int i=1; i<=n; ++i) a[i] ^= X[i%4];
	}
	return 0;
}


// maxround[i][j] ~ 选手 i 的实力为 j 时,满足条件 1 的最大轮数 
int maxround[100001][17];
// cround[i] ~ i 对应的比赛结点的轮数(r)
int cround[262144];
// 获胜的 c 上界(s) 
int ceiling[262144];
// 获胜者能力值(p) 
int power[262144];
// larray[i] 即 i 对应的 l 值,预处理降常 
int larray[131073];

// 模拟法求 maxround ,复杂度 O(nlogn) 
void make_maxround(){
	for(int i=1; i<=n; ++i){
		for(int j=0; j<K; ++j){
			maxround[i][j] = K;
		}
		int curr = dpref(K)+i;
		int competion = fa(curr);
		// 双指针,即使不用,写成 O(n(logn)^2) 也是可以接受的 
		int p = 0;
		for(int round=1; round<=K; ++round){
			if(d[competion]==lr(curr)){
				for(int j=p; j<round; ++j){
					maxround[i][j] = round-1;
				}
				p = round;
			}
			curr = competion;
			competion = fa(curr);
		}
	}
}
// 求 cround ,复杂度 O(n)
void make_cround(int id, int round){
	cround[id] = round;
	if(id<=dpref(K)){
		make_cround(ls(id), round-1);
		make_cround(rs(id), round-1);
	}
}
void make_larray(){
	// 注意,i=1 时 , l=1 
	larray[1] = 1;
	for(int i=2; i<=(1<<K); ++i){
		int l = 1;
		if(i!=1){
			for(; l<i; l<<=1);
			l = (l>>1)+1;
		}
		larray[i] = l;
	}
}
void init(){
	make_maxround();
	make_cround(1, K);
	make_larray();
}

// 重置 ceiling(s), power(p)
void clear(int id){
	ceiling[id] = n;
	power[id] = -1;
	if(id<=dpref(K)){
		clear(ls(id));
		clear(rs(id));
	}
}

// 更新 power[id] 后计算影响 
void update(int id, int time){
	if(id==1) return;
	// 注意不要遗漏能确定 power(fa[id]) 的情况 
	if(d[fa(id)]==lr(id)){
		if(power[id]>=cround[fa(id)]){
			ceiling[bro(id)] = min(ceiling[bro(id)], time-1);
			power[fa(id)] = power[id];
			update(fa(id), time);
		}else if(power[bro(id)]!=-1){
			power[fa(id)] = power[bro(id)];
			update(fa(id), time);
		}
	}else if(power[bro(id)]!=-1 && power[bro(id)]<cround[fa(id)]){
		power[fa(id)] = power[id];
		update(fa(id), time);
	}
}
// 向下传递 ceiling 
void push_down(int id){
	if(id<=dpref(K)){
		ceiling[ls(id)] = min(ceiling[ls(id)], ceiling[id]);
		ceiling[rs(id)] = min(ceiling[rs(id)], ceiling[id]);
		push_down(ls(id));
		push_down(rs(id));
	}
}

int64 result[100002];
void solve(){
	clear(1);
	for(int i=1; i<=n; ++i){
		power[dpref(K)+i] = a[i];
		update(dpref(K)+i, i);
		// 注意 result 差分数组也要清空 
		result[i] = 0;
	}
	push_down(1);
	for(int i=1; i<=dsize(K); ++i){
		int l = larray[i];
		// 条件 2 
		int r = ceiling[dpref(K)+i];
		// 条件 1 ,注意前提是 a[i] 是确定的值,a[i]>=K 时不会限制 
		if(i<=n && r>=i && a[i]<K){
			// min(r) 是必要的,条件 1 限制的上界可能大于条件 2 限制的上界 
			r = min(r, 1<<maxround[i][a[i]]);
			// 注意,当且仅当 r>=i 时才能用条件 1 限制 r ,应该 min(i-1) 修正 
			r = max(r, i-1);
		}
		if(r>=l){
			result[l] += i;
			result[r+1] -= i;
		}
	}
	for(int i=1; i<=n; ++i){
		result[i] += result[i-1];
	}
	int64 ans = 0;
	for(int i=1; i<=m; ++i){
		ans ^= result[c[i]]*i;
	}
	printf("%lld\n", ans);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值