题目大意
给定一个
n
个点,
- 选择一个棋子
- 选择一个颜色集合
- 与这个点连出去且颜色在集合中的边所连向的所有点棋子数量+1
- 删除这个棋子
无法操作的人输。问先手是否必胜。
Data Constraint
题解
一道博弈题。根据SG定理,最后的SG就是所有棋子所在结点的SG异或和。若>0则先手必胜;=0先手必败。
现在问题是如何求每个点的SG。先注意到SG是2的幂这一性质。对于一个结点,它可以扩展出许多状态,所以这个结点的SG就是这些状态的MEX,每个状态的SG则是各种颜色的结点异或和得来。
于是现在问题变成,给定若干个数,任意选择这些数异或起来,所不能得到的最小的自然数是多少。
于是通过构造线性基就能求出SG了。
SRC
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<bitset>
#include<vector>
using namespace std ;
#define N 200 + 10
#define M 5000 + 10
bitset < M > f[M] , t ;
vector < int > G[N][M] ;
bool flag[N][M] , ans[M] ;
int Node[M] , Next[M] , Head[N] , tot ;
int D[N] , R[N] , SG[N] , C[N][M] ;
int n , m , Q , Col ;
void link( int u , int v ) {
Node[++tot] = v ;
Next[tot] = Head[u] ;
Head[u] = tot ;
}
void BFS() {
int i = 0 , j = D[0] ;
while ( i < j ) {
i ++ ;
int now = D[i] ;
for (int p = Head[now] ; p ; p = Next[p] ) {
R[Node[p]] -- ;
if ( R[Node[p]] == 0 ) D[++j] = Node[p] ;
}
}
D[0] = j ;
}
int main() {
scanf( "%d%d" , &n , &m ) ;
for (int i = 1 ; i <= m ; i ++ ) {
int u , v , w ;
scanf( "%d%d%d" , &u , &v , &w ) ;
link( u , v ) ;
R[v] ++ ;
G[u][w].push_back(v) ;
if ( !flag[u][w] ) {
flag[u][w] = 1 ;
C[u][++C[u][0]] = w ;
}
Col = max( Col , w ) ;
}
for (int i = 1 ;i <= n ; i ++ ) if ( !R[i] ) D[++D[0]] = i ;
BFS() ;
for (int i = D[0] ; i ; i -- ) {
int now = D[i] ;
memset( f , 0 , sizeof(f) ) ;
for (int j = 1 ; j <= C[now][0] ; j ++ ) {
t.reset() ;
for (int k = 0 ; k < G[now][C[now][j]].size() ; k ++ ) {
int s = G[now][C[now][j]][k] ;
t[SG[s]] = t[SG[s]] ^ 1 ;
}
for (int k = n ; k >= 0 ; k -- ) {
if ( t[k] == 0 ) continue ;
if ( f[k][k] == 0 ) { f[k] = t ; break ; }
t ^= f[k] ;
}
}
for (int j = 1 ; j <= n ; j ++ ) {
if ( f[j][j] == 0 ) { SG[now] = j ; break ; }
}
}
scanf( "%d" , &Q ) ;
for (int i = 1 ; i <= Q ; i ++ ) {
int x ;
scanf( "%d" , &x ) ;
ans[SG[x]] ^= 1 ;
}
for (int i = 0 ; i <= Col ; i ++ ) if ( ans[i] ) { printf( "1\n" ) ; return 0 ; }
printf( "0\n" ) ;
return 0 ;
}
以上.