Bzoj 2115: [Wc2011] Xor

16 篇文章 0 订阅
4 篇文章 0 订阅

原题网址:http://www.lydsy.com/JudgeOnline/problem.php?id=2115
这里写图片描述
Input

第一行包含两个整数N和 M, 表示该无向图中点的数目与边的数目。 接下来M 行描述 M 条边,每行三个整数Si,Ti ,Di,表示 Si 与Ti之间存在 一条权值为 Di的无向边。 图中可能有重边或自环。

Output

仅包含一个整数,表示最大的XOR和(十进制结果),注意输出后加换行回车。

Sample Input

5 7

1 2 2

1 3 2

2 4 1

2 5 1

4 5 3

5 3 4

4 3 2
Sample Output

6
这里写图片描述

本题因为可以重复走点和边,所以要用异或的性质就是两次异或会抵消。所以本题有个性质就是任何一条路径 1 ~n的异或和都可以当成 1 ~n的一条路径加上若干个环(因为你可以走在路径上,在某一点出发去一个环,在环上走一圈,原路返回,轨迹有了一个环,而且走向环的路径被抵消掉了)。图里环的数量也特别大,把图当成一棵树,每走一条返祖边便统计一个环,假设图边数为 m ,这样统计到的环是O(m)级别的,有一个性质是图中所有环都可以用这些环异或得到(我不会证,只会意会)。
于是简化问题后,我们有一个数必选,其他有若干个数可以选,最终要取一些数使得异或和最大。这时候要用到一个东西叫线性基。当判断一个二进制是否可以用一堆数异或起来时,可以将这些数简化到 logD 级别,这里的 D 表示数的大小。具体方法:从高位到地位每次选一个这位上是1的数,作为一个线性基,将这个数把其他这位上是 1 的数都异或一遍(有点像高斯消元),最后得到的logD个数便是线性基,这些线性基的最高位各不相同,每次只要从高位到低位把你需要改变的位所对应的线性基异或上去,最后得到的值便是异或的最大值。
之所以WA了好多次是因为当把 1 左移到int64级别这个1不能直接用 1 ,要么强转,要么定义一个int64类型的存1的变量。否则会有奇怪的运算错误。

type
  edge=record
         x,y,next:longint;
         v:int64;
       end;
const
  MAXN=200050;
var
  map:array[0..MAXN] of edge;
  vis:array[0..MAXN] of boolean;
  first:array[0..MAXN] of longint;
  path,c:array[0..MAXN] of int64;
  x:array[0..63] of int64;
  n,m,s,i,a,b,j,k,cnt:longint;
  e,v:int64;
procedure ins(x,y:longint;v:int64);
  begin
    inc(s);map[s].x:=x;map[s].y:=y;map[s].v:=v;
    map[s].next:=first[x];first[x]:=s;
  end;
procedure dfs(x:longint);
  var
    t,y:longint;
  begin
    vis[x]:=true;
    t:=first[x];
    while (t>0) do
      begin
        y:=map[t].y;
        if (not vis[y])
          then
            begin
              path[y]:=path[x] xor map[t].v;
              dfs(y);
            end
          else
            begin
              inc(cnt);
              c[cnt]:=path[x] xor map[t].v xor path[y];
            end;
        t:=map[t].next;
      end;
  end;
begin
  read(n,m);
  e:=1;
  s:=0;
  for i:=1 to m do
    begin
      read(a,b,v);
      ins(a,b,v);
      ins(b,a,v);
    end;
  fillchar(vis,sizeof(vis),false);
  cnt:=0;
  dfs(1);
  fillchar(vis,sizeof(vis),false);
  for i:=63 downto 0 do
    begin
      x[i]:=0;
      for j:=1 to cnt do
        if (not vis[j])and(c[j] and (e << i)>0) then
          begin
            x[i]:=c[j];vis[j]:=true;
            for k:=1 to cnt do
              if (not vis[k])and(c[k] and (e << i)>0)
                then c[k]:=c[k] xor c[j];
            break;
          end;
    end;
  for i:=63 downto 0 do
    if (path[n] and (e << i)=0)
      then path[n]:=path[n] xor x[i];
  writeln(path[n]);
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值