Codeforces 1087F Rock-Paper-Scissors Champion

版权声明:博主写博客也挺不容易,转载当然阔以,记得先吱一声~ https://blog.csdn.net/Cold_Chair/article/details/85254728

传送门。

题解:

比赛要回宿舍去睡觉没时间做了。

GD选手表示只会强行数据结构。

首先先来猜一下结论。

对一个人,左右分开考虑,只有都能赢,才能赢。

一边能赢的条件是,要么不存在克制它的,要么存在克制它的同时存在它克制的。

这个是很显然的。

既然带修,强行套上一个线段树,对于线段树的每个区间维护f[l][r]表示左边传过来的状态是l,右边传过来的状态是r,这个区间有多少个可行的,状态是二进制状态,表示每个手势是否出现过。

时间复杂度是 O ( 64   n   l o g n ) O(64~n~logn) ,显然n=1e5时CF的2s戳戳有余。

实际上可以直接维护三个set,分别表示三种手势出现的位置,然后就可以大分类讨论(没脑选手逃~)

Code:

#include<cstdio>
#include<cstring>
#define gc getchar
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define i0 i + i
#define i1 i + i + 1
#define pr printf
using namespace std;

char read() {
	char c = gc();
	while(c < 'A' || c > 'Z') c = gc();
	return c;
}

char C;

const int N = 2e5 + 5;

int n, q, x, a[N], a2[3];
int b[3][8][8];
int t[N * 4][8][8], z[N * 4], pc;

void add(int i, int x, int y, int c, int l, int r) {
	if(x == y) {
		z[i] = a2[pc];
		fo(j, 0, 7) fo(k, 0, 7) t[i][j][k] = b[pc][j][k];
		return;	
	}
	int m = x + y >> 1;
	if(c <= m) {
		add(i0, x, m, c, l, r | z[i1]);
	} else {
		add(i1, m + 1, y, c, l | z[i0], r);
	}
	fo(j, 0, 7) fo(k, 0, 7) {
		t[i][j][k] = t[i0][j][k | z[i1]] + t[i1][j | z[i0]][k];
	}
	z[i] = z[i0] | z[i1];
}

int main() {
//	freopen("a.in", "r", stdin);
	a2[0] = 1; a2[1] = 2; a2[2] = 4;
	scanf("%d %d", &n, &q);
	fo(i, 0, 2) {
		fo(j, 0, 7) fo(k, 0, 7) {
			int li = (i + 2) % 3, ni = (i + 1) % 3;
			int yy = 1;
			#define p(x, y) ((x & a2[y]) > 0)
			yy &= !(p(j, ni) && !p(j, li));
			yy &= !(p(k, ni) && !p(k, li));
			b[i][j][k] = yy;
		}
	}
	fo(i, 1, n) {
		C = read();
		a[i] = C == 'R' ? 0 : (C == 'P' ? 1 : 2); 
	}
	fo(i, 1, n) {
		pc = a[i];
		add(1, 1, n, i, 0, 0);
	}
	pr("%d\n", t[1][0][0]);
	fo(ii, 1, q) {
		scanf("%d", &x); C = read();
		pc = C == 'R' ? 0 : (C == 'P' ? 1 : 2);
		add(1, 1, n, x, 0, 0);
		pr("%d\n", t[1][0][0]);
	}
}

猜你喜欢

转载自blog.csdn.net/Cold_Chair/article/details/85254728