POJ 3710
树的删边游戏。。
由于题目的特殊性,我们只需计算环的边数值。若为偶环,则直接把环的根节点置0。若为奇环,则留下一条边与根结点相连,并那它们的SG置0;
注意的是,两个点也可构成环,因为允许重边。所以,我们只需求点双连通分量,并判断分量中边的数量即可。然后DFS求树的SG值。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 5 6 using namespace std; 7 8 const int N=110; 9 const int M=1100; 10 int n,m; 11 struct { 12 int v,next; 13 }edge[M]; 14 struct { 15 int u,v; 16 }edge_stack[M],tmp; 17 int dfn[N],low[N],index; 18 int edge_top,tot; 19 bool vis[N],vis_e[M]; 20 int head[N],sg[N]; 21 22 void addedge(int u,int v){ 23 vis_e[tot]=false; 24 edge[tot].v=v; 25 edge[tot].next=head[u]; 26 head[u]=tot++; 27 } 28 29 void tarjan(int u){ 30 int i,j,k,v,e; 31 dfn[u]=low[u]=++index; 32 for(e=head[u];e!=-1;e=edge[e].next){ 33 v=edge[e].v; 34 if(dfn[v]==-1){ 35 vis_e[e]=vis_e[e^1]=true; 36 edge_stack[++edge_top].u=u; 37 edge_stack[edge_top].v=v; 38 tarjan(v); 39 low[u]=min(low[u],low[v]); 40 if(dfn[u]<=low[v]){ 41 int cnt=0; 42 do{ 43 tmp.u=edge_stack[edge_top].u; 44 tmp.v=edge_stack[edge_top].v; 45 edge_top--; 46 cnt++; 47 vis[tmp.u]=vis[tmp.v]=true; 48 // printf("edge=%d %d ",tmp.u,tmp.v); 49 }while(!(tmp.u==u&&tmp.v==v)); 50 // printf("\n"); 51 if((cnt&1)){ 52 vis[tmp.u]=vis[tmp.v]=false; 53 } 54 else vis[tmp.u]=false; 55 } 56 } 57 else{ 58 if(!vis_e[e]){ 59 low[u]=min(low[u],dfn[v]); 60 if(dfn[u]>dfn[v]){ 61 edge_stack[++edge_top].u=u; 62 edge_stack[edge_top].v=v; 63 } 64 } 65 } 66 } 67 } 68 69 int dfs(int u){ 70 int e,v; 71 vis[u]=true; 72 int ans=sg[u]; 73 for(e=head[u];e!=-1;e=edge[e].next){ 74 int v=edge[e].v; 75 if(!vis[v]){ 76 ans^=(dfs(v)+1); 77 } 78 } 79 return ans; 80 } 81 82 int main(){ 83 int k,u,v; 84 while(scanf("%d",&k)!=EOF){ 85 int ans=0; 86 while(k--){ 87 edge_top=-1; 88 scanf("%d%d",&n,&m); 89 tot=index=0; 90 for(int i=1;i<=n;i++){ 91 vis[i]=false; sg[i]=0; 92 head[i]=dfn[i]=low[i]=-1; 93 } 94 for(int i=1;i<=m;i++){ 95 scanf("%d%d",&u,&v); 96 addedge(u,v); 97 addedge(v,u); 98 } 99 tarjan(1); 100 ans^=dfs(1); 101 } 102 if(ans) printf("Sally\n"); 103 else printf("Harry\n"); 104 } 105 return 0; 106 }