Time Limit: 6000/4000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/Others) |
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<math.h>
#include<iostream>
#include<string>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);}
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1,class T2>inline void gmax(T1 &a,T2 b){if(b>a)a=b;}
template <class T1,class T2>inline void gmin(T1 &a,T2 b){if(b<a)a=b;}
const int N=5e4+10,M=2e5+10,Z=1e9+7,ms63=1061109567;
int casenum,casei;
int n,m,x,y;LL z;
int first[N];int id;
int w[M],nxt[M];LL c[M];
LL f[N],cir[M>>1];int g;
int dfn[N];int tim;
void ins(int x,int y,LL z)
{
++id;
w[id]=y;
c[id]=z;
nxt[id]=first[x];
first[x]=id;
}
void dfs(int x)
{
dfn[x]=++tim;
for(int z=first[x];z;z=nxt[z])
{
int y=w[z];
if(dfn[y]==0)
{
f[y]=f[x]^c[z];
dfs(y);
}
else if(dfn[y]>dfn[x])
{
LL tmp=f[x]^c[z]^f[y];
if(tmp)cir[++g]=tmp;
}
}
}
int main()
{
scanf("%d",&casenum);
for(casei=1;casei<=casenum;++casei)
{
scanf("%d%d",&n,&m);
memset(first,0,(n+2)*4);id=0;
for(int i=1;i<=m;i++)
{
scanf("%d%d%lld",&x,&y,&z);
ins(x,y,z);
ins(y,x,z);
}
memset(dfn,0,(n+2)*4);tim=0;
g=0;f[1]=0;dfs(1);
LL ans=0;
for(int i=59;i>=0;i--)
{
LL bit=1ll<<i;
LL v=-1;
for(int j=1;j<=g;j++)if(cir[j]&bit)
{
v=cir[j];
break;
}
if(~v)
{
gmax(ans,ans^v);
for(int j=1;j<=g;j++)if(cir[j]&bit)cir[j]^=v;
}
}
printf("Case #%d: %lld\n",casei,ans);
if(g>2*m-(n-1))while(1);
}
return 0;
}
/*
【trick&&吐槽】
1,把图上的问题转化为树上,真是好办法啊
2,题目中提到2^60-1,而且还是对于异或值而言,于是我们应该想到按位从高到低贪心或高斯消元
3,因为反向边的干扰,这道题可能的环数不只是m-(n-1),而可能高达2m-(n-1),我们可以通过时间戳的方法简化
【题意】
T(1<=T<=30)组数据
对于每组数据,给你一个联通图,图上有n(2<=n<=5e4)个点,有m(1<=m<=1e5)条双向带权边。
然后问你这个图上异或和最大的环。
【类型】
高斯消元
【分析】
首先,我们从一点出发,再回到这个点,如果一条链走了2次(或者说偶数次),那么异或值变成了0
如果我们想产生不为0的异或值,需要走一个环走奇数次,也就是从某条路过去再从另外一条路回来。
我们发现,可以把任意一个环的异或和引入为我们的答案。
于是,我们如果可以得到所有环的异或和,把这些数来做高斯消元求最大异或和,答案也就出来了。
然而,想求出所有环,是会TLE的。
于是我们转化为求本质不同的环。
因为这个图是联通的,所以事实上从一个点就可达所有环。
于是我们干脆从点1开始做dfs,记录从1到每个点的异或值f[],
一旦从一个点沿边出发能够到达另外一个已经记录了f[]的点,那么我们就可以得到一个环的权值,
权值为f[x]^c[z]^f[y],我们记录下这所有本质不同的环。
这可以看做是由联通图的生成树加边所构成的环。所以环的数量g不会超过m-(n-1)
之后问题就变成了——
"给你g个数,让你在其中取出任意数量的数,使得选择的数的异或值尽可能大"这个问题。
这个问题要如何解决呢?类似于高斯消元的做法——
我们对于一个较高的位,肯定是"能选1的话一定贪心为1"
如果当前位为0,那么我们可以选择任意一个此位为1的数v。
然后把ans异或上这个数v,并把所有此位为1的数都异或消掉v。来使得后面低位的选数不会再对高位产生干扰。
如果当前位为1,那么我们肯定不会选择此位为1的数。
然而为了使得后面低位的选数不会再对这位产生干扰,我们还是要选择任意一个此位为1的数v。并把所有此位为1的数都异或消掉v。
这样这道题就可以AC啦。
这题是2015CCPC难度第9易的题目,做完这题就能拿到金牌了。
【时间复杂度&&优化】
O(n+m+60(m-n))
*/