BZOJ2115-XOR-线性基+图环

2115: [Wc2011] Xor

Time Limit: 10 Sec   Memory Limit: 259 MB
Submit: 4762   Solved: 1985
[ Submit][ Status][ Discuss]

Description

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

HINT

Source

题意:

给定一个 n(n≤50000) 个点 m(m≤10000) 条边的无向图,

每条边上有一个权值。请你求一条从 1 到 n 的路径,使得路径上的边的异或和最大。

题解:

这道题要求从1到n的最大XOR的路径,存在重边,允许经过重复点重复边

那么在图上作图尝试之后就会发现,路径一定是由许多的环和一条从1到n的路径组成。容易发现,来回走是没有任何意义的,因为来回走意味着抵消。考虑这道题求得是路径xor和最大,所以必然我们要想办法处理环的情况。

任意地先找出一条从1到n的路径,把这条路径上的xor和作为ans初值(先不管为什么可行),然后我们的任务就变成了求若干个环与这个ans初值所能组合成的xor最大值。显然,我们需要预处理出图上所有的环,并处理出所有环的环上xor值,这当然是dfs寻找,到n的路径的时候顺便求一下就可以了。

任意地先找出一条从1到n的路径,把这条路径上的xor和作为ans初值(先不管为什么可行),然后我们的任务就变成了求若干个环与这个ans初值所能组合成的xor最大值。显然,我们需要预处理出图上所有的环,并处理出所有环的环上xor值,这当然是dfs寻找,到n的路径的时候顺便求一下就可以了。

现在我们来讨论上述做法的可行性。

  第一种情况:我们对最终答案产生贡献的某个环离1到n的主路径很远,这样的话,因为至少可以保证1可以到达这个环,那么我们可以走到这个环之后绕环一周之后原路返回,这样从1走到环的路上这一段被重复经过所以无效,但是环上的xor值被我们得到了,所以我们并不关心这个环和主路径的关系,我们只关心环的权值。

  

        第二种情况:我们任意选取的到n的路径是否能保证最优性。假设存在一条更优的路径从1到n,那么这条路径与我们原来的路径构成了一个环,也就会被纳入线性基中,也会被计算贡献,假如这个环会被经过,那么最后的情况相当于是走了两遍原来选取的路径,抵消之后走了一次这个最优路径,所以我们无论选取的是哪条路径作为ans初值,都可以通过与更优情况构成环,然后得到一样的结果。这一证明可以拓展到路径上的任意点的路径选取。

转自:https://www.cnblogs.com/ljh2000-jump/p/5869925.html

代码:

/**
    给定一个 n(n≤50000) 个点 m(m≤10000) 条边的无向图,
    每条边上有一个权值。请你求一条从 1 到 n 的路径,使得路径上的边的异或和最大。


*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e4+10;
const int maxm = 1e6+10;
typedef struct node {
    int to,next;
    ll val;
    node(){};
    node(int t,int ne,ll v):to(t),next(ne),val(v){};
}Edge;
Edge edge[maxm];
int head[maxn],tot,cnt;
bool vis[maxn];
ll circle[maxm],dist[maxn];
int readi() {
    char ch = getchar();int t = 0;
    while(ch < '0' || ch > '9') ch = getchar();
    while(ch >= '0' && ch <= '9') t = (t<<3)+(t<<1)+ch-48,ch = getchar();
    return t;
}
ll readl() {
    char ch = getchar(); ll t = 0;
    while(ch < '0' || ch > '9') ch = getchar();
    while(ch >= '0' && ch <= '9') t = (t<<3)+(t<<1)+ch-48,ch = getchar();
    return t;
}
void addedge(ll val,int v,int u) {
    //printf("%d %d %lld\n",u,v,val);
    edge[++tot] = (Edge){v,head[u],val};head[u]=tot;
    edge[++tot] = (Edge){u,head[v],val};head[v]=tot;
}
void init() {
    memset(head,-1,sizeof(head));
    memset(vis,0,sizeof(vis));
    memset(dist,0,sizeof(dist));
    memset(circle,0,sizeof(circle));
    cnt = tot = 0;
}
void dfs(int u) {
    vis[u] = 1;
    for(int i=head[u];~i;i=edge[i].next) {
        int v = edge[i].to;
        if(!vis[v]) {
            dist[v] = dist[u]^edge[i].val;
            dfs(v);
        }
        else circle[++cnt] = dist[u] ^ dist[v] ^ edge[i].val;
    }
}

const int MaxBasis = 63;
struct LB {
    ll base[MaxBasis+10]; bool rel; int sz;
    vector<ll> Basis;
    LB() { memset(base, 0, sizeof(base)); rel = false; sz = 0; Basis.clear();}
    void init() {
        memset(base,0,sizeof(base));rel = false;sz = 0;Basis.clear();
    }
    bool add(ll x) {				///加入线性基中
        for (int i = MaxBasis; i >= 0; --i) {
            if (!(x >> i & 1)) continue;
            if (base[i]) x ^= base[i];
            else {
                for (int j = 0; j < i; ++j) if (x >> j & 1) x ^= base[j];
                for (int j = i+1; j <= MaxBasis; ++j) if (base[j] >> i & 1) base[j] ^= x;
                base[i] = x, ++sz;
                return true;
            }
        }
        rel = true; /// 表示有一个没插入进去,则说明当前线性基能表示出x,则如果放入x存在异或为0的结果。
        return false;
    }
    ll Max(ll ans = 0) {			///取最大
        for(int i=0;i<=MaxBasis;i++) ans = max(ans,ans^base[i]);
        return ans;
    }
    ll Min(ll ans = 0) {			///取最小
        for(int i=0;i<=MaxBasis;i++) ans = min(ans,ans^base[i]);
        return ans;
    }
    void GetBasis() {				///构造向量,用于之后求第k小
        for (int i = 0; i <= MaxBasis; ++i) if (base[i]) Basis.push_back(base[i]);
    }
    void umerge(struct LB &b) {			///合并线性基
        for(int i=0;i<=MaxBasis;i++) if(b.base[i])
            add(b.base[i]);
    }
    ll Min_Kth(ll k) {				///线性基中第k小
        if(rel) k--;				///线性基未满存在0
        if(k >= ((ll)1<<sz)) return -1;
        ll ans = 0;
        for(int i=0;i<sz;i++) if(k & ((ll)1<<i)){
            ans ^= Basis[i];
        }
        return ans;
    }
}lb;

int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        init();
        for(int i=1;i<=m;i++) {
            addedge(readl(),readi(),readi());
        }

        dfs(1);
        lb.init();
        ll ans = dist[n];
        for(int i=1;i<=cnt;i++) lb.add(circle[i]);
        printf("%lld\n",lb.Max(ans));
    }
    return 0;
}


猜你喜欢

转载自blog.csdn.net/m0_38013346/article/details/80250913
今日推荐