【题解】割cut [牛客Wannafly挑战赛1 E]
【题解】割cut [牛客Wannafly挑战赛1 E]
传送门:割 \(\text{cut}\) [牛客 \(\text{Wannafly}\) 挑战赛 \(\text{1 E}\)]
【题目描述】
给定一个 \(n\) 个点 \(m\) 条边的无向简单图(无重边无自环),每条边都有权值 \(w\) 。图的一个割指:将点集划分为两个不重不漏的集合 \(S\) 和 \(T\) 。割的权值为所有两个端点分别属于 \(S\) 和 \(T\) 的边的权值异或和(即 \(S\) 内部的边和 \(T\) 内部的边不算)。
现求这个图的割的所有可能的权值之和,输出答案前 \(9\) 位(不足 \(9\) 位则全部输出)。
【输入输出样例】
样例输入:
2 1
1 2 1
样例输出:
1
【数据范围】
对于 \(20 \%:\) \(n\leqslant 20,\) \(m\leqslant 400\) 。
另外 \(20 \%:\) \(m=n-1,\) 保证读入的图是一棵树。
另外 \(20 \%:\) \(w\leqslant 16\) 。
对于 \(100 \%:\) \(1\leqslant n\leqslant 10^5,\) \(1\leqslant m\leqslant \min(\frac{n(n-1)}{2},2\times10^5),\) \(0\leqslant w\leqslant 10^9\)
【分析】
这题被放到了今天 \(\text{NOI}\) 模拟赛 \(\text{day2 T2}\)。
样例差评 样例差评 样例差评 样例差评 样例差评
题目描述差评 题目描述差评 题目描述差评 题目描述差评 题目描述差评
题目要求的是割所有可能的权值之和,不是所有割的权值之和!
一开始题目理解错误,头都快想炸了还是没有思路,只差没破口大骂了。
对于 \(n\leqslant 20\) 的部分直接 \(2^n\) 枚举割的划分方式,然后枚举所有边求权值。复杂度 \(2^nm\approx O(4\times 10^8)\),显然会超时(实际效率不清楚,或许常数小能卡过?),考虑把枚举边改为枚举两个点集,于是降为 \(\sum_{i=1}^{n}C_{n}^{i}i(n-i)\approx n^22^{n-2}\approx O(10^8)\) 。
对于一棵树的情况,发现任意选出一些边砍掉都可以划分成割(因为一定能划成二分图),于是题意变为:给出 \(m\) 个值,求任选一些值可能的异或和之和。
显然要挂一个线性基。但问题在于如何快速求所有异或和之和,\(O(2^{size})\) 暴力枚举显然是不行的,考虑按位处理:对于每一位 \(i\),若存在某个基的第 \(i\) 位为 \(1\),那么答案加上 \(2^i2^{size-1}\) 。
正确性证明:
任选一个第 \(i\) 位为 \(1\) 的基 \(p\) 把它去掉,在剩下的 \(size-1\) 个基中进行选择,易知有 \(2^{size-1}\) 种不同的异或和,设异或和第 \(i\) 位为 \(0\) 的有 \(cnt_0\) 种,为 \(1\) 的有 \(cnt_1\) 种(显然 \(cnt_0+cnt_1=2^{size-1}\))。现在把它们都异或上 \(p\),则又得到 \(cnt_0\) 种第 \(i\) 位为 \(1\) 的,\(cnt_1\) 种第 \(i\) 位为 \(0\) 的。故第 \(i\) 位为 \(1\) 的共有 \(cnt_0+cnt_1=2^{size-1}\) 种。
对于一般的无向简单图,考虑将每个点所连的边权值都异或起来,并作为这个点的点权,那么点集 \(S\)(或者 \(T\))的点权异或和就是割的权。
正确性证明:
若边的两个端点在不同点集,那么该边权只会被异或一次,反之会被异或两次消掉(即变为 \(0\)) 。
于是问题转换为:任选一些点作为点集 \(S\),计算其可能的点权异或和之和。
发现和上面那个问题一模一样,只是插入线性基的值不同了。
还剩下最后一个问题:怎么输出前 \(9\) 位?
总不可能现场写一个高精吧.....于是我去问了问教练,发现 \(\text{std}\) 直接开 \(\text{ull}\) 就过了,珂啪。
时间复杂度为:\(O(m+\log^{2}inf)\) 。
(\(w\leqslant 16\) 的部分分是给那些只会 \(O(2^{size})\) 枚举线性基统计答案的人准备的)
【Code】
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define LL unsigned long long
#define Re register int
using namespace std;
const int N=1e5+3,M=2e6+3;
int n,m,x,y,z,o=1,head[N];LL ans;
struct QAQ{int w,to,next;}a[M<<1];
inline void add(Re x,Re y,Re z){a[++o].w=z,a[o].to=y,a[o].next=head[x],head[x]=o;}
inline void in(Re &x){
int f=0;x=0;char ch=getchar();
while(ch<'0'||ch>'9')f|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
x=f?-x:x;
}
struct JI{
int cnt,p[33];
inline void insert(Re x){
for(Re i=30;i>=0;--i)
if((x>>i)&1){
if(!p[i]){++cnt,p[i]=x;break;}
x^=p[i];
}
}
}ji;
int cnt;
inline void print(LL x){
if(x>9)print(x/10);
putchar(x%10+'0');
if(++cnt>=9)exit(0);
}
int main(){
// freopen("cut.in","r",stdin);
// freopen("cut.out","w",stdout);
in(n),in(m);
for(Re i=1;i<=m;++i)in(x),in(y),in(z),add(x,y,z),add(y,x,z);
for(Re x=1;x<=n;++x){
Re v=0;
for(Re i=head[x];i;i=a[i].next)v^=a[i].w;
ji.insert(v);
}
for(Re i=0;i<=30;++i){
Re flag=0;
for(Re j=0;j<=30;++j)if(ji.p[j])flag|=((ji.p[j]>>i)&1);
if(flag)ans+=(LL)(1<<i)*(1<<(ji.cnt-1));
}
print(ans);
fclose(stdin);
fclose(stdout);
return 0;
}