数树师

损坏的传送门

题目大意

有一棵个 n n n 点的树,每条边有边权,定义 f i , j f_{i,j} fi,j 为点 i i i 到点 j j j 的路径上所有边的边权的按位与( & 值), g i , j g_{i,j} gi,j 为点 i i i 到点 j j j 的路径上所有边的边权的按位或( | 值),求 ∑ i = 1 n ∑ j = i + 1 n f i , j × g i , j \sum_{i=1}^{n} \sum_{j=i+1}^{n} f_{i,j} \times g_{i,j} i=1nj=i+1nfi,j×gi,j 998244353 998244353 998244353 的值。

解题思路

首先看到按位或和按位与和这毒瘤的数据范围,就可以想到只能用 二 进 制 \color{red}二进制 来解。

类似于这道题 → \to 奇怪的最短路

题面

给定长度为 n n n 的数列 a a a,如果 a i   a n d   a j ​ ! = 0 a_i \ and \ a_j ​!= 0 ai and aj!=0(按位与),则在 i i i j j j 之间存在一条长度为 a i ​ + a j a_i​ + a_j ai+aj​ 的边,求 1 1 1 至所有点的最短路。

普及一下:

按位与运算( a n d and and

定义:参加运算的两个数据,按二进制位进行"与"运算。

运算规则:

0&0=0 ,0&1=0 ,1&0=0 ,1&1=1

总结:两位同时为 1 1 1,结果才为 1 1 1,否则结果为 0 0 0

例如: 3   a n d   5 3 \ and \ 5 3 and 50000 0011 & 0000 0101 = 0000 0001,因此 3   a n d   5 3 \ and \ 5 3 and 5 的值得 1 1 1

按位或运算符( o r or or

定义:参加运算的两个对象,按二进制位进行"或"运算。

运算规则:

0|0=0 ,0|1=1 ,1|0=1 ,1|1=1

总结:参加运算的两个对象只要有一个为 1 1 1,其值为 1 1 1

例如: 3   o r   5 3 \ or \ 5 3 or 50000 0011 | 0000 0101 = 0000 0111,因此, 3   o r   5 3 \ or \ 5 3 or 5 的值得 7 7 7

对于二进制,首先想到的就是 拆 位 \color{red}拆位

类似于乘法分配律,我们可以枚举第 i i i 位和 第 j j j 位,计算有多少对 x , y x,y x,y 满足 f x , y f_{x,y} fx,y 的第 i i i 位和 f x , y f_{x,y} fx,y 的第 j j j 位都为 1 1 1,那么答案即为 x , y x,y x,y 的对数乘上 2 i + j 2^{i+j} 2i+j

先看第 i i i 位为 1 1 1 f x , y f_{x,y} fx,y,因为我们希望最后按位与得到的结果第 i i i 位为 1 1 1,那么路径上的边权第 i i i 位一定都是 1 1 1。所以我们只保留边权的第 i i i 位为 1 1 1 的边,那么在每个连通块内的任意两点 < u , v > <u,v> <u,v> 满足 f u , v f_{u,v} fu,v 的第 i i i 位为 1 1 1,用并查集维护即可。

再看第 j j j 位为 1 1 1 g x , y g_{x,y} gx,y 的个数,显然,这很难求,因为 o r or or 运算不好处理为 1 1 1 的情况,那就需要处理为 0 0 0 的情况。

根据 容 斥 原 理 \color{red}容斥原理 ,易得 f x , y f_{x,y} fx,y i i i 位和 g x , y g_{x,y} gx,y j j j 位都为 1 1 1 的情况数 = = = f x , y f_{x,y} fx,y i i i 位为 1 1 1 的情况数 − - f x , y f_{x,y} fx,y i i i 位为 1 1 1 g x , y g_{x,y} gx,y j j j 位为 0 0 0 的情况数。

十 年   O I   一 场 空 , 不 开   l o n g   l o n g   见 祖 宗 \color{red}{十年 \ OI \ 一场空,不开 \ long \ long \ 见祖宗}  OI  long long 

AC CODE

快读终于缩短了,祭

#include <bits/stdc++.h>
using namespace std;

#define int long long
#define _ (int) 5e4 + 5
#define MOD 998244353

struct Fastio
{
    template <typename T>
    inline Fastio operator>>(T &x)
    {
        x = 0;
        char c = getchar();
        while (c < '0' || c > '9')
            c = getchar();
        while (c >= '0' && c <= '9')
            x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        return *this;
    }
    template <typename T>
    inline Fastio &operator<<(T x)
    {
        if (x == 0)
        {
            putchar('0');
            return *this;
        }
        if (x < 0) putchar('-'), x = -x;
        static int sta[45];
        int top = 0;
        while (x) sta[++top] = x % 10, x /= 10;
        while (top) putchar(sta[top] + '0'), --top;
        return *this;
    }
} oi;

int n, ans;

int u[_], v[_], w[_];

int fa[_], siz[_];

void init()
{
    for (int i = 1; i <= n; i++)
    {
        fa[i] = i;
        siz[i] = 1;
    }
}

int find(int x)
{
    return x == fa[x] ? x : fa[x] = find(fa[x]);
}

void merge(int x, int y)
{
    x = find(x), y = find(y);
    if (x != y)
    {
        fa[y] = x;
        siz[x] += siz[y];
    }
}

signed main()
{
    oi >> n;
    for (int i = 1; i < n; ++i) oi >> u[i] >> v[i] >> w[i];
    for (int i = 0; i <= 20; i++)
    {
        for (int j = 0; j <= 20; j++)
        {
            int cnt1 = 0, cnt2 = 0;
            init();
            for (int k = 1; k < n; k++)
                if (w[k] & (1 << i)) // 第 i 位和第 j 位都为 1 的
                {
                    merge(u[k], v[k]);
                }
            for (int k = 1; k <= n; k++)
                if (k == find(k))
                {
                    cnt1 = (cnt1 + (siz[k] - 1) * siz[k] / 2 % MOD) % MOD;
                }
            init();
            for (int k = 1; k < n; k++)
                if (w[k] & (1 << i)) 
                {
                    if (!(w[k] & (1 << j))) // 第 i 位为 1,第 j 位为 0 的
                    {
                        merge(u[k], v[k]);
                    }
                }
            for (int k = 1; k <= n; k++)
                if (k == find(k))
                {
                    cnt2 = (cnt2 + (siz[k] - 1) * siz[k] / 2 % MOD) % MOD;
                }
            ans = (ans + (1ll << (i + j)) % MOD * (cnt1 - cnt2 + MOD) % MOD) % MOD;
        }
    }
    oi << ans;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值