当前位置:网站首页>B-杰哥的树(状压树形dp)

B-杰哥的树(状压树形dp)

2022-07-06 12:18:00 朴小明

链接:杰哥的树

题意:

给定一棵树,边权 是 {a,b,c,d,e,f} 这六个字符,求不同的路径数,要求路径上的字符都恰好出现偶数次。

做法:

只关心奇偶,那么一个字符就可有两个状态——1代表奇数个,0代表偶数个,要想同时表示六个字符是奇数还是偶数,就可用0~63来表示。

定义 f[x][i] 为以第 x 个节点为起点,状态为 i 有多少路径数量,如果 x 的儿子是 y ,当前字符是 ch ,那么 f\;[\;x\;][\;i\;]\; += f[\;y\;][\;i \oplus (1 << (ch - 'a'))\;],除了由儿子的状态转移而来,还会有新的状态,不继承儿子的状态,xy 的这条边也可单独作为一个状态,则当i == (1<<(ch - 'a'))时, f\;[\;x\;][\;i\;]++

还要考虑点x作为中间节点的情况,这个简单,由f[x][i] 计算即可。

代码:

struct tree
{
    int y, val;
};
vector<tree> v[N];
int f[N][1ll << 6];
int ans;
void dfs(int x, int fa)
{
    f[x][0] = 0;
    for (auto tt : v[x])
    {
        int y = tt.y, val = tt.val;
        if (y == fa) continue;
        dfs(y, x);
        for (int i = 0; i < (1ll << 6); i++)
        {
            f[x][i] += f[y][i ^ (1ll << val)];
            if (i == (1ll << val)) f[x][i]++;   //不能乱加1,之所以加1,是因为定义x这个点只出一条边
        }
    }
    ans += f[x][0];
    int sum = 0;
    for (auto tt : v[x])
    {
        int y = tt.y, val = tt.val;
        if (y == fa) continue;
        for (int i = 0; i < (1ll << 6); i++)
        {
            int x1 = f[x][i] - f[y][i ^ (1ll << val)] - (i == (1ll << val));
            int x2 = f[y][i ^ (1ll << val)] + (i == (1ll << val));
            sum += x1 * x2;
        }
    }
    ans += sum / 2;
}
void solve()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n - 1; i++)
    {
        int x, y;
        char c;
        scanf("%lld%lld", &x, &y);
        cin >> c;
        v[x].emplace_back(tree{y, c - 'a'});
        v[y].emplace_back(tree{x, c - 'a'});
    }
    dfs(1, 0);
    cout << ans << "\n";
}

原网站

版权声明
本文为[朴小明]所创,转载请带上原文链接,感谢
https://blog.csdn.net/ATXTKS/article/details/125156547