USTC_1130

    时隔多日,回过头在再做这个题目时终于AC了,于是顿时觉得能力的提升确实需要时间的积淀。

    首先,如果Alice会输,那么各个棋子所在位置的sg函数值的异或必然为0,于是我们可以先预处理出各个节点的sg函数值。

    至于求方案,一开始的想法就是去dp了,比如用f[i][j][k]表示到第i个节点时放了j个棋子,且它们异或值为k的方案总数。这样i的上限是100,j是10000,k是128,这样的复杂度显然是不能承受的。

    我们联想到异或的性质,偶数个同一个数的异或为0,因此,整个局面的sg函数值,实际上只与每个节点上棋子数目的奇偶性有关,而剩下的棋子则两个两个的看成一组,放在哪里就无所谓了,可以用组合数算出方案数的。

    于是我们只要用f[i][j][k]表示到第i个节点时奇数个棋子的节点数为j,它们的异或值为k的的方案总数。这样j的上限就是100了。之后对于每个f[N][j][0],如果S-j是偶数,就用组合数算一下剩下的棋子摆放的方案数,再和f[N][j][0]乘起来之后,加到最终结果里即可。

#include<stdio.h>
#include<string.h>
#define MAXD 110
#define MAXM 10010
#define MOD 1000000007
int N, M, S, U, V, e, first[MAXD], next[MAXM], v[MAXM], ny[MAXD], sg[MAXD], ch[MAXD][MAXD], f[MAXD][MAXD][130];
void exgcd(long long a, long long b, long long &x, long long &y)
{
    if(b == 0)
        x = 1, y = 0;
    else
        exgcd(b, a % b, y, x), y -= x * (a / b);
}
void prepare()
{
    int i;
    long long x, y;
    for(i = 1; i <= 100; i ++)
    {
        exgcd(i, MOD, x, y);
        x = (x % MOD + MOD) % MOD;
        ny[i] = x;
    }
}
long long comb(int n, int m)
{
    int i;
    long long ans = 1;
    for(i = n - m + 1; i <= n; i ++)
        ans = ans * i % MOD;
    for(i = 2; i <= m; i ++)
        ans = ans * ny[i] % MOD;
    return ans;
}
void add(int x, int y)
{
    v[e] = y;
    next[e] = first[x], first[x] = e ++;
}
void init()
{
    int i, x, y;
    scanf("%d%d%d", &N, &M, &S);
    e = 0;
    memset(first, -1, sizeof(first));
    for(i = 0; i < M; i ++)
    {
        scanf("%d%d", &x, &y);
        ++ x, ++ y;
        add(x, y);
    }
}
void dfs(int cur)
{
    int i;
    memset(ch[cur], 0, sizeof(ch[cur]));
    for(i = first[cur]; i != -1; i = next[i])
    {
        if(sg[v[i]] == -1)
            dfs(v[i]);
        ch[cur][sg[v[i]]] = 1;
    }
    for(i = 0; ch[cur][i]; i ++);
    sg[cur] = i;
}
void solve()
{
    int i, j, k;
    long long ans = 0;
    memset(sg, -1, sizeof(sg));
    for(i = 1; i <= N; i ++)
        if(sg[i] == -1)
            dfs(i);
    memset(f[0], 0, sizeof(f[0]));
    f[0][0][0] = 1;
    for(i = 1; i <= N; i ++)
        for(j = 0; j <= i; j ++)
            for(k = 0; k <= 128; k ++)
                f[i][j][k] = (f[i - 1][j][k] + f[i - 1][j - 1][k ^ sg[i]]) % MOD;
    for(i = 0; i <= N; i ++)
        if((S - i) % 2 == 0)
            ans = (ans + f[N][i][0] * comb((S - i) / 2 + N - 1, N - 1)) % MOD;
    printf("%lld\n", ans);
}
int main()
{
    int t;
    prepare();
    scanf("%d", &t);
    while(t --)
    {
        init();
        solve();
    }
    return 0;
}
posted on 2012-05-22 00:12  Staginner  阅读(152)  评论(0编辑  收藏  举报