题目描述
给定一个无向简单图(即无重边无自环). 每条边都有一个权值. 这个图的一个鸽, 指的是将它的点集划分为两个不重不漏的集合S和T. 这个鸽的权值, 是所有两个端点分别属于S和T的边的权值的异或和(即, S内部的边和T内部的边都不算). 现在问这个图的鸽的所有可能权值的和是多少. 由于这个数很大, 只需要输出前9位, 不足9位则全部输出.
输入描述:
第一行两个数n和m表示图的点数和边数(0<n<100001,0<m<200001).
之后m行每行3个数表示一条边的两个端点和这条边的权值. 点的编号从1到n,权值为0到10^9之间的整数.
输出描述:
输出答案
示例1
输入
2 1
1 2 1
输出
1
说明
给定一个无向简单图(即无重边无自环). 每条边都有一个权值. 这个图的一个鸽, 指的是将它的点集划分为两个不重不漏的集合S和T. 这个鸽的权值, 是所有两个端点分别属于S和T的边的权值的异或和(即, S内部的边和T内部的边都不算). 现在问这个图的鸽的所有可能权值的和是多少. 由于这个数很大, 只需要输出前9位, 不足9位则全部输出.
输入描述:
第一行两个数n和m表示图的点数和边数(0<n<100001,0<m<200001).
之后m行每行3个数表示一条边的两个端点和这条边的权值. 点的编号从1到n,权值为0到10^9之间的整数.
输出描述:
输出答案
示例1
输入
2 1
1 2 1
输出
1
说明
所有的鸽中,只有把1和2分开的鸽才有非0的权值, 这个权值只能是1. 所以所有可能的权值的和是0+1=1.
参考大神:http://www.cnblogs.com/wmrv587/p/7667243.html
分析:
比赛题解为:一个割的权值就是集合中点权值的异或(点权值就是和他相邻的边权值的异或。)(集合取割的两个集合中哪一个都行。)理由是这样的话集合内的边恰好算了0或者2次,集合之间的算了一次。这样就转化成一些数字随便选一些异或起来的可能答案和,就很好统计了。
?????怎么求答案和????
还要去学一波 线性基!
大神说:先求出线性基,设秩为r
枚举每一位,看看是否有某个基该位是1,如果有1,那么这些基能异或出的2^r个数中,该位是1的一定有一半,所以该位的贡献就是$2^{r-1}*2^i$
怎么理解呢,就是 把 r 个线性基 组合 起来的异或值 在 该 i 位上为1的组合 一定有 2^(r-1)种。
为什么? 设 r 个基中 i位为1的有 a个,为0的有b个,既 a+b=r, 那么异或值i位为1的条件就是 从 a中选奇数个与b随便组合:
既: 2^b*{C(a,1)+C(a,3)+C(a,5)+... }= 2^b* 2^(a-1) = 2 ^(a+b-1) = 2^(r-1)// 不知道证明是否有错 ,有望指点。
#include<iostream> #include<stdio.h> #include<string.h> #include<algorithm> #include<vector> const int maxn = 100000; typedef long long ll; using namespace std; int n,m,a[maxn+5]; int pos[32]; //线性基 + 求贡献。 //参考大神代码:https://www.nowcoder.com/acm/contest/view-submission?submissionId=19070357 //大神的 博客:http://www.cnblogs.com/wmrv587/p/7667243.html int main() { scanf("%d%d",&n,&m); for(int i=1; i<=m; i++) { int x,y,w; scanf("%d %d %d",&x,&y,&w); a[x]^=w, a[y]^=w; } for(int i=1; i<=n; i++)//求线性基 { for(int j=30; j>=0; j--) { if(a[i]&(1<<j)) { if(!pos[j]) { pos[j] = a[i]; break; } a[i]^=pos[j]; } } } int num = 0; for(int j=0;j<=30;j++) if(pos[j]) num++; num--; ll ans = 0; for(int j=0;j<=30;j++)// 每一位 对答案的贡献。 { int flag = 0; for(int i=0;i<=30;i++) if(pos[i]&(1<<j)) flag = 1; if(flag) ans+=(1ll<<j); } ans<<=num; while (ans>=1000000000) ans/=10; printf("%lld\n",ans); return 0; }