数树师

  • Post author:
  • Post category:其他



损坏的传送门



题目大意

有一棵个



n

n






n





点的树,每条边有边权,定义



f

i

,

j

f_{i,j}







f











i


,


j






















为点



i

i






i





到点



j

j






j





的路径上所有边的边权的按位与(

&

值),



g

i

,

j

g_{i,j}







g











i


,


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


=


1










n


































j


=


i


+


1










n






















f











i


,


j





















×









g











i


,


j


























998244353

998244353






9


9


8


2


4


4


3


5


3





的值。



解题思路

首先看到按位或和按位与和这毒瘤的数据范围,就可以想到只能用



\color{red}二进制


















来解。

类似于这道题



\to













奇怪的最短路

题面

给定长度为



n

n






n





的数列



a

a






a





,如果



a

i

 

a

n

d

 

a

j

!

=

0

a_i \ and \ a_j ​!= 0







a










i




















a


n


d





a










j





















!




=








0





(按位与),则在



i

i






i









j

j






j





之间存在一条长度为



a

i

+

a

j

a_i​ + a_j







a










i























+









a










j





















​ 的边,求



1

1






1





至所有点的最短路。

普及一下:

按位与运算(



a

n

d

and






a


n


d





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

运算规则:

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




a


n


d




5







0000 0011 & 0000 0101 = 0000 0001

,因此



3

 

a

n

d

 

5

3 \ and \ 5






3




a


n


d




5





的值得



1

1






1





按位或运算符(



o

r

or






o


r





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

运算规则:

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




o


r




5







0000 0011 | 0000 0101 = 0000 0111

,因此,



3

 

o

r

 

5

3 \ or \ 5






3




o


r




5





的值得



7

7






7





对于二进制,首先想到的就是



\color{red}拆位















类似于乘法分配律,我们可以枚举第



i

i






i





位和 第



j

j






j





位,计算有多少对



x

,

y

x,y






x


,




y





满足



f

x

,

y

f_{x,y}







f











x


,


y






















的第



i

i






i





位和



f

x

,

y

f_{x,y}







f











x


,


y






















的第



j

j






j





位都为



1

1






1





,那么答案即为



x

,

y

x,y






x


,




y





的对数乘上



2

i

+

j

2^{i+j}







2











i


+


j













先看第



i

i






i





位为



1

1






1









f

x

,

y

f_{x,y}







f











x


,


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}







f











u


,


v






















的第



i

i






i





位为



1

1






1





,用并查集维护即可。

再看第



j

j






j





位为



1

1






1









g

x

,

y

g_{x,y}







g











x


,


y






















的个数,显然,这很难求,因为



o

r

or






o


r





运算不好处理为



1

1






1





的情况,那就需要处理为



0

0






0





的情况。

根据



\color{red}容斥原理





















,易得



f

x

,

y

f_{x,y}







f











x


,


y


























i

i






i





位和



g

x

,

y

g_{x,y}







g











x


,


y


























j

j






j





位都为



1

1






1





的情况数



=

=






=








f

x

,

y

f_{x,y}







f











x


,


y


























i

i






i





位为



1

1






1





的情况数



















f

x

,

y

f_{x,y}







f











x


,


y


























i

i






i





位为



1

1






1









g

x

,

y

g_{x,y}







g











x


,


y


























j

j






j





位为



0

0






0





的情况数。





 

O

I

 

 

l

o

n

g

 

l

o

n

g

 

\color{red}{十年 \ OI \ 一场空,不开 \ long \ long \ 见祖宗}

















O


I




























l


o


n


g






l


o


n


g





















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;
}



版权声明:本文为qq_46258139原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。