44 分求 de
查看原帖
44 分求 de
36580
L_RUA楼主2024/11/6 19:12

44 分求 de,大致思路是从下往上讨论,为了方便处理每次处理 2R2^R 次方长度的区间,然后和之前答案完全固定的区间做合并,具体的比较部分都写了注释,看了一下午不知道哪里有问题

#include <bits/stdc++.h>

using namespace std;

#define int long long 

inline int read(){
	int x = 0, f = 1;
	char ch = getchar();
	//一直没有读到数字,注意处理负号 
	while (ch < '0' || ch > '9') {
		if(ch == '-') {
			f = -1;	
		}
		ch = getchar();
	}
	//一位位读进数字 
	while (ch >= '0' && ch <= '9') {
		//等价于 x * 10 + ch - '0' 
		x = (x << 3) + (x << 1) + ch - '0';
		ch = getchar();
	}
	//符号乘上数字 
	return x * f;
}

const int maxn = 100005;
const int maxp = 20;

struct SegmentTree{
	int l;
	int r;
	int winner;
	int w_val;
	int sum;
} tr[maxn << 3];

int n, m, K;
int a[maxn], val[maxn];
int X[4];

int d[maxp][maxn << 1];
char D[maxp][maxn << 1]; 

int tr_id[maxn];

int q[maxn];
int ans[maxn];
int lose_sum, nxt_pos;
int lst; 

void pre() {
	for (int i = 1; i <= n; ++i) {
		val[i] = a[i] ^ X[i % 4];
	} 
}

void push_up(int x) {
	tr[x].sum = tr[x << 1].sum + tr[x << 1 | 1].sum;
}

void build(int x, int l, int r, int offset) {
	tr[x].l = l;
	tr[x].r = r;
	tr[x].winner = 0;
	tr[x].w_val = 0;
	if (l == r) {
		tr[x].sum = l + offset;
		tr_id[l + offset] = x;
		return;
	}
	int mid = (l + r) >> 1;
	build(x << 1, l, mid, offset);
	build(x << 1 | 1, mid + 1, r, offset);
	push_up(x);
}

void ins(int x, int val, int p, int offset) {
	tr[x].winner = tr[x].sum;
	tr[x].w_val = val;
	int R = 1; 
	while (x != 1) {
		//父亲节点 
		int prt = x >> 1;
		int now_p = (p + 1) / 2;
		//编号小,左儿子 
		if ((prt << 1) == x) {
			//当前是擂主 
			if(d[R][now_p] == 0) {
				//胜出,那么右儿子所有都不可能取胜 
				if (tr[x].w_val >= R) { 
					tr[prt].winner = tr[x].winner;
					tr[prt].w_val = tr[x].w_val;
					nxt_pos = tr[prt].r + offset;
					lose_sum += tr[prt << 1 | 1].sum;
				} else { //失败,那么右儿子情况未知,只能减去当前的价值
					lose_sum += tr[x].winner;
					break; 
				}
			} else {
				//不是擂主的话,因为是左儿子,右边都是未知信息,跳过 
				break;
			}
		} else { //编号大,右儿子
			//当前是擂主 
			if (d[R][now_p] == 1) {
				//作为擂主胜出,那么左儿子之前的胜者不可能再取胜 
				if (tr[x].w_val >= R) {
					tr[prt].winner = tr[x].winner;
					tr[prt].w_val = tr[x].w_val;
					lose_sum += tr[prt << 1].winner;
				} else { //否则,左儿子之前的胜者将继续比赛 
					tr[prt].winner = tr[prt << 1].winner;
					tr[prt].w_val = tr[prt << 1].w_val;
					lose_sum += tr[x].winner; 
				}
			} else { //不是擂主的话,说明左边之前作为擂主输了,继续向上
				tr[prt].winner = tr[x].winner;
				tr[prt].w_val = tr[x].w_val;
			}
		} 
		//轮数增加 
		x = prt;
		p = now_p;
		++R;
	}
}

void solve(int l, int r, int R) {
	if (d[R][1] == 0) {
		if (val[lst] >= R) {
			for (int i = l; i <= r; ++i) {
				ans[i] = lst;
			}
			return;
		}
	}
//	printf("R: %lld, lst: %lld\n", R, lst);
	build(1, 1, r - l + 1, l - 1);
	int sum = (r + l) * (r - l + 1) / 2;
//	printf("sum: %lld\n", sum);
	lose_sum = 0;
	for (int i = l; i <= min(r, n); ++i) {
		nxt_pos = i;
		ins(tr_id[i], val[i], i, l - 1);
//		printf("id: %lld, lose_sum: %lld\n", i, lose_sum);
		nxt_pos = min(nxt_pos, n);
		for (int j = i; j <= nxt_pos; ++j) {
			ans[j] = sum - lose_sum;
			if (d[R][1] == 1) {
				if (tr[1].winner && tr[1].w_val < R) {
					ans[j] = lst;
				}
				if (!tr[1].winner) {
					ans[j] += lst;	
				} 
			}
		}
		i = nxt_pos;
	}
	lst = ans[r];
}

signed main() {
	n = read();
	m = read();
	for (int i = 1; i <= n; ++i) {
		a[i] = read();
	}
	for (int i = 1; i <= m; ++i) {
		q[i] = read();
	}
	K = (int) ceil(log2(n));
	for (int i = 0; i < K; ++i) {
		scanf("%s", D[i + 1]);
	}
	for (int i = 1; i <= K; ++i) {
		int len = strlen(D[i]);
		for (int j = 0; j < len; ++j) {
			d[i][j + 1] = D[i][j] - '0';
		}
	}
	int T = read();
	while (T--) {
		for (int i = 0; i < 4; ++i) {
			X[i] = read();
		}
		pre();
		ans[1] = 1;
		lst = 1;
		for (int i = 1; i <= K; ++i) {
			solve((1 << (i - 1)) + 1, 1 << i, i);
		}
		
//		printf("ans: ");
//		for (int i = 1; i <= n; ++i) {
//			printf("%lld ", ans[i]);
//		}
//		puts("");
		
		int final_ans = 0;
		for (int i = 1; i <= m; ++i) {
			final_ans = final_ans ^ (i * ans[q[i]]);
		}
		printf("%lld\n", final_ans);
	}
	
	return 0;
}
2024/11/6 19:12
加载中...