【题解】割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;
}
posted @ 2020-08-12 14:03  辰星凌  阅读(400)  评论(0编辑  收藏  举报