【bzoj2115】 Xor

www.lydsy.com/JudgeOnline/problem.php?id=2115 (題目鏈接)

題意:給出一張圖,可能有重邊和自環,在圖中找出一條從1~n的路徑,使得經過的路徑的權值的異或和最大,每條邊可以重複經過並且重複計算異或和。

Solution
  剛看到這道題,想了10分鐘完全沒有思路,於是就膜了題解。
  我們先把圖看成一棵樹,那麼我們所需要找出的路徑可以分成一些環和一條從1~n的樹上路徑,至於樹是怎麼構造的以及那條從1~n的路徑是怎麼走的並不重要,因爲我們可以通過在總路徑上補環來增大異或和。於是這個問題就轉化爲了如何在環的異或和中選出一些使得這些值異或上從1~n的路徑的異或和最大。
  於是我們先dfs一遍把環以及它的異或和摳出來,當然這些環可能並不完全,但它們可以通過互相異或來異或出沒有得到的環。如果發現當前節點x的目標節點e[i].to已經被走過了,那麼這個環的異或和就是dis[x]^dis[e[i].to]^e[i].w。
  怎麼求最大異或和呢?這需要用到一個很神的東西:線性基
  
什麼是線性基:

  若干數的線性基是一組數a1,a2,…an,其中ax的最高位的1在第x位。通過線性基中元素xor出的數的值域與原來的數xor出數的值域相同。
  線性基主要有2個優秀的性質:
  可以O(k)(二進制的位數)的求出能異或出的最大值,或者是判斷一個數能否被異或出來。

線性基的構造:

  線性基的構造有兩種:
  第一是高斯消元。通過高斯消元將二進制矩陣消成上三角矩陣或者是對角矩陣,當然消成後者時間複雜度略高,但也更爲方便,求解異或和最大時一路for下去就可以了。而上三角矩陣需要判斷如果答案異或上這個值能否使答案增大。
  第二種就是一種擬陣貪心,詳情參見jump大爺的博客——www.cnblogs.com/ljh2000-jump/p/5869991.html,因爲證明太高深,這裏也就不再贅述。

  所以這道題就很好做了對吧。

代碼:

// bzoj2115
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define inf 2147483640
#define Pi 3.1415926535898
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;

const int maxn=50010,maxm=100010;
struct edge {int to,next;LL w;}e[maxm<<1];
LL dis[maxn],c[maxm<<2],bin[65];
int vis[maxn],n,m,tot,sum,cnt,head[maxn];

void link(int u,int v,LL w) {
    e[++cnt].to=v;e[cnt].next=head[u];head[u]=cnt;e[cnt].w=w;
    e[++cnt].to=u;e[cnt].next=head[v];head[v]=cnt;e[cnt].w=w;
}
void dfs(int x) {
    vis[x]=1;
    for (int i=head[x];i;i=e[i].next) {
        if (!vis[e[i].to]) {
            dis[e[i].to]=dis[x]^e[i].w;
            dfs(e[i].to);
        }
        else c[++sum]=dis[x]^dis[e[i].to]^e[i].w;
    }
}
void Gauss() {
    for (LL i=bin[60];i;i>>=1) {
        int j=tot+1;
        while (j<=sum && !(c[j]&i)) j++;
        if (j==sum+1) continue;
        swap(c[++tot],c[j]);
        for (j=1;j<=sum;j++) if (j!=tot && c[j]&i) c[j]^=c[tot];
        //for (int k=j+1;k<=sum;k++) if (c[k]&i) c[k]^=c[tot];
    }
}
int main() {
    bin[0]=1;for (int i=1;i<=60;i++) bin[i]=bin[i-1]<<1;
    scanf("%d%d",&n,&m);
    for (int u,v,i=1;i<=m;i++) {
        LL w;
        scanf("%d%d%lld",&u,&v,&w);
        link(u,v,w);
    }
    dfs(1);
    Gauss();
    LL ans=dis[n];
    for (int i=1;i<=tot;i++) ans=max(ans,ans^c[i]);
    printf("%lld\n",ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章