设一个 ( exttt{RYB}) 字符串混合的结果为做如下操作直到字符串长度 (le 1):若开头两个字符不同,将其替换为一个与这两个字符都不同的字符;否则将这两项删去。
若最终序列长度 (=1) 则结果即为唯一的字符,否则为 ( exttt W)。
有一个长为 (n) 的字符串,其中有些位置是空的,(k) 次操作:
- ( exttt{mix m j_1 j_2 }cdots exttt{ j_m c}):将第 (j_1,cdots,j_m) 个字符按顺序混合,得到字符 (c)。
- ( exttt{RY/RB/YB m j_1 j_2 }cdots exttt{ j_m}):将第 (j_1,cdots,j_m) 个字符交换 ( exttt{RY/RB/YB})。
构造出合法的原字符串。需判断无解。
(n,kle 10^3)。
很容易发现 ( exttt{W/.RYB}) 代表 (0,1,2,3),混合即为异或和。然后发现不太好处理交换操作。
不容易发现交换操作是线性变换,因此我们可以把每个数拆成两个基底 (a_i,b_i) 的线性组合,初始时 (a_i=1,b_i=2),则 ( exttt{RY/RB/YB}) 分别是 swap(a[i],b[i])
,b[i] ^= a[i]
,a[i] ^= b[i]
。
然后解一个 (2n) 元 (2k) 个方程的方程组即可,时间复杂度是三方的。
#include<bits/stdc++.h>
using namespace std;
const int N = 2003;
template<typename T>
void read(T &x){
int ch = getchar(); x = 0;
for(;ch < '0' || ch > '9';ch = getchar());
for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
int n, m, k, a[N], b[N], ans[N];
bitset<N> bas[N];
void ins(bitset<N> val){
for(int i = 0;i < (n<<1);++ i) if(val[i]){
if(bas[i].none()){bas[i] = val; return;}
val ^= bas[i];
} if(val[n<<1]){puts("NO"); exit(0);}
}
int ID(char x){
switch(x){
case 'R': return 1;
case 'Y': return 2;
case 'B': return 3;
default: return 0;
}
}
int main(){
read(n); read(k);
for(int i = 0;i < n;++ i){a[i] = 1; b[i] = 2;}
while(k --){
char opt[4], col[2]; scanf("%s%d", opt, &m);
if(opt[0] == 'm'){
bitset<N> e1, e2;
while(m --){
int x; read(x); -- x;
e1[x<<1] = a[x]&1; e1[x<<1|1] = a[x]>>1;
e2[x<<1] = b[x]&1; e2[x<<1|1] = b[x]>>1;
} scanf("%s", col);
int id = ID(col[0]);
e1[n<<1] = id&1; e2[n<<1] = id>>1;
ins(e1); ins(e2);
} else if(opt[0] == 'R' && opt[1] == 'Y'){
while(m --){int x; read(x); -- x; swap(a[x], b[x]);}
} else if(opt[0] == 'R' && opt[1] == 'B'){
while(m --){int x; read(x); -- x; b[x] ^= a[x];}
} else {
while(m --){int x; read(x); -- x; a[x] ^= b[x];}
}
}
for(int i = (n<<1)-1;~i;-- i){
ans[i] = bas[i][n<<1];
for(int j = i-1;~j;-- j)
if(bas[j][i]) bas[j] ^= bas[i];
} puts("YES");
for(int i = 0;i < n;++ i) putchar(".RYB"[ans[i<<1]|ans[i<<1|1]<<1]);
}