版权声明:博主写博客也挺不容易,转载当然阔以,记得先吱一声~ https://blog.csdn.net/Cold_Chair/article/details/85254728
题解:
比赛要回宿舍去睡觉没时间做了。
GD选手表示只会强行数据结构。
首先先来猜一下结论。
对一个人,左右分开考虑,只有都能赢,才能赢。
一边能赢的条件是,要么不存在克制它的,要么存在克制它的同时存在它克制的。
这个是很显然的。
既然带修,强行套上一个线段树,对于线段树的每个区间维护f[l][r]表示左边传过来的状态是l,右边传过来的状态是r,这个区间有多少个可行的,状态是二进制状态,表示每个手势是否出现过。
时间复杂度是 ,显然n=1e5时CF的2s戳戳有余。
实际上可以直接维护三个set,分别表示三种手势出现的位置,然后就可以大分类讨论(没脑选手逃~)
Code:
扫描二维码关注公众号,回复:
5034469 查看本文章
#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]);
}
}