2020牛客暑期多校训练营(第五场) Graph

原题
题目描述
W先生得到了一个具有N个顶点和N-1条边的新图。 这是一个没有环的连通图。 每个边缘应具有难看的值。 为了使图形更美观,W先生希望您可以帮助他进行修改。 您可以一次删除或添加一个具有丑陋值的边,并且可以根据需要进行多次。 但是任何时候都应满足以下条件:
1 、 1、 1 图形已连接。
2 、 2、 2 对于图中的每个环,该环中所有丑陋值的XOR总和为0。
W先生想知道图中所有边的丑陋值的最小和。
输入描述:
第一行包含一个整数N (2≤N≤100000)。
接下来的N-1行每行包含三个整数U,V,W (0≤U,V30),表示顶点U和V之间的边界为丑值W。
输出描述:一个整数,图形中所有边的丑陋值的最小和。
样例
输入

6
0 1 1
1 2 4
1 3 3
0 4 5
0 5 2

输出

7

思路
脑袋一拍我们会知道这可以围成任意一棵树,而且任意两个点之间连边的权值都是固定的。
因为图始终都是联通的,所以任意两点间始终存在至少一条路径。
如果存在多条,根据环的异或和为0,两点间的路径的异或和应该相等,且始终是固定的。
我们可以给每个点一个权值,那么两点间的连边权值就应该是两端点权的异或。
所以问题就转化为求异或最小生成树,用Boruvka算法即可。
还不懂的可以看看代码。
代码

#include
using namespace std;
const int M=1e5+5;
int a1,b1,c1,n,num=1,cnt,m[30],h[M],a[2*M],o[60*M],q[60*M],s[60*M][2];
long long ans;
struct node{int v,nxt,w;}e[2*M];
long long find(int x,int y)
{
    if(q[x]==30)return a[o[x]]^a[o[y]];
    bool flag=0;long long ret=2e9;
    for(int i=0;i<=1;i++)if(s[x][i]&&s[y][i])ret=min(ret,find(s[x][i],s[y][i])),flag=1;
    if(flag==0)
    {
        if(s[x][0]&&s[y][1])ret=min(ret,find(s[x][0],s[y][1]));
		else if(s[x][1]&&s[y][0])ret=min(ret,find(s[x][1],s[y][0]));
    }
    return ret;
}
void dfs(int x)
{
    if(!x) return;
    dfs(s[x][0]);dfs(s[x][1]);
    if(s[x][0]&&s[x][1])ans+=find(s[x][0],s[x][1]);
}
void get(int x,int fa){for(int i=h[x];i;i=e[i].nxt)if(e[i].v!=fa)a[e[i].v]=a[x]^e[i].w,get(e[i].v,x);}
int main()
{
    o[1]=1;m[0]=1;scanf("%d",&n);
    for(int i=1;i<=29;i++)m[i]=m[i-1]*2;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%d",&a1,&b1,&c1);
		e[++cnt].v=++b1;e[cnt].w=c1;e[cnt].nxt=h[++a1];h[a1]=cnt;
		e[++cnt].v=a1;e[cnt].w=c1;e[cnt].nxt=h[b1];h[b1]=cnt;
    }
    get(1,0);
    for(int i=1,x=1;i<=n;i++,x=1) 
	{
        for(int j=29;j>=0;j--)
        {
            int p=1<<j;
			bool tmp=p&a[i];
            if(s[x][tmp]==0)s[x][tmp]=++num,q[num]=29-j+1,o[num]=n+1;
            x=s[x][tmp];
        }
        o[x]=i;
	}
	dfs(1);
    printf("%lld",ans);
}

你可能感兴趣的:(2020牛客暑期多校训练营(第五场) Graph)