题目链接:这里
题意:有多棵树进行删边博弈,注意这里的”树“可能存在环,形状也许是非常诡异的。
解法:下面来自cxlove神的博客
我们利用The Fusion Principle:任何环内的节点可以融合成一点而不会改变图的sg值。(下面我们称它为融合原则)
融合原则允许我们把任意一个根图简化为一个等效的可以通过冒号原则(即Colon Principle)简化为竹竿的树。
我们会发现,拥有奇数条边的环可简化为一条边,偶数条边的环可简化为一个节点。
在这一步中很明显要求我们能找到图中的环,而且判断环中节点的个数,进行处理。
利用Tarjan算法找出强连通分量,毕竟不是搞图论的,现学现用。。。理解不深
可能出现重边,对于重边的处理是,如果两点间有偶数条边,则当作偶数环处理,将其化为一个点,否则保留。
将图转化成我们所要的树之后,便是经典的删边游戏了。
叶子节点的SG值为0;中间节点的SG值为它的所有子节点的SG值加1 后的异或和。
最后把多棵树一起做 一次NIM便完成。
上面叙述的内容其实来自IOI2009中国国家集训队论文,是石家庄二中的贾志豪同学的论文。然后这个题目是里面的例题,这篇论文写得很强。可以去看:这里下载

代码

//POJ 3710 Tarjan求连通分量+树形博弈删边游戏

#include <vector>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
vector <int> E[150]; //存图
int mat[150][150]; //存边的数量
int low[105], dfn[105], s[105], top, clk;
bool instack[105], vis[105]; // 在Tarjan找环之后,把不需要的点标记掉
void Tarjan(int u, int fa){
    low[u] = dfn[u] = ++clk;
    s[top++] = u;
    instack[u] = 1;
    for(int i = 0; i < E[u].size(); i++){
        int v = E[u][i];
        if(v == fa && mat[u][v] > 1){//判断重边
            if(mat[u][v] % 2 == 0) vis[u] = 1;
            continue;
        }
        if(!dfn[v]){
            Tarjan(v, u);
            low[u] = min(low[u], low[v]);
        }
        else if(v != fa && instack[v]){
            low[u] = min(low[u], dfn[v]);
        }
    }
    if(low[u] == dfn[u]){
        int cnt = 1;
        top--;
        while(s[top] != u){
            vis[s[top]] = 1;
            top--;
            cnt++;
        }
        if(cnt&&(cnt&1)) vis[s[top+1]] = false;
    }
}
int getsg(int u, int fa){
    int ret = 0;
    for(int i = 0; i < E[u].size(); i++){
        int v = E[u][i];
        if(!vis[v] && v != fa){
            ret ^= (1 + getsg(v, u));
        }
    }
    return ret;
}
int main()
{
    int k, n, m;
    while(scanf("%d", &k) != EOF)
    {
        int ret = 0;
        while(k--){
            scanf("%d%d", &n, &m);
            for(int i = 1; i <= n; i++) E[i].clear();
            memset(mat, 0, sizeof(mat));
            memset(low, 0, sizeof(low));
            memset(dfn, 0, sizeof(dfn));
            memset(instack, 0, sizeof(instack));
            memset(vis, 0, sizeof(vis));
            top = clk = 0;
            while(m--){
                int u, v;
                scanf("%d%d", &u, &v);
                mat[u][v]++, mat[v][u]++;
                E[u].push_back(v), E[v].push_back(u);
            }
            Tarjan(1, -1);
            ret ^= getsg(1, -1);
        }
        puts(ret?"Sally":"Harry");
    }
    return 0;
}