• 【图论,数学 + 线性基】AcWing 228. 异或


    分析

    思路很有价值的一道题。

    首先我们可以注意到,一种解的形式一定是一条 \(1\to n\) 的路径以及若干个环(当然可以是 \(0\) 个)。

    因此下面只需要证明两个结论:

    1. 一条 \(1\to n\) 的路径(的异或值)一定能够被另一条 \(1\to n\)​ 的路径以及若干个环拼凑出来。
    2. 任意一个能够被起点到达的环(的异或值)一定能被选取。

    证明

    • 对于第一个结论:可以发现如果 \(1\to n\)​ 存在两条路径,那么这两条路径之间一定能形成一个环,那么我们一定能从一条路径 + 环异或得到另一条路径。
    • 第二个结论比较明显,如果要选那个环,就从起点出发到那个环上绕一圈再回到起点即可。

    有了上面两个结论,我们就能够保证这个做法正确性:任意选取一条 \(1\to n\)​ 的路径的异或值,然后将其异或上若干个环(下面说具体做法)并得到最优解(也就是所需要求的解)。

    具体做法:

    以下图为例:
    image

    我们从 \(1\)​​ 出发做一遍 \(\texttt{dfs}\)​​,然后将第一次访问的到的点所对应的边记为树边,否则记为环边

    例如 \(\texttt{dfs}\) 访问的过程中经过点的顺序为 \(1 \to 2 \to 3\to 4 \to 5\),那么树边(绿色)和环边(红色)如下图所示:

    image

    当我们得到环边的时候,将相应的环加入一个集合 \(ring\) 中,比如下图标出来的环边(黄色的边)对应的环(橙色标注)就是:
    image

    可以发现,这样能够保证所有环能够被直接或间接地异或得到,也就保证了正确性。(例如环 \(\{ 2, 3, 5, 4\}\) 可以被 \(\{ 2, 3, 4\}, \{ 3, 4, 5\}\) 异或得到)

    最后的任务是从集合 \(ring\)​ 中选若干个以及任意一条初始路径进行异或使得答案最大,这里可以使用线性基来解决。

    实现

    // Problem: 异或
    // Contest: AcWing
    // URL: https://www.acwing.com/problem/content/230/
    // Memory Limit: 64 MB
    // Time Limit: 1000 ms
    // 
    // Powered by CP Editor (https://cpeditor.org)
    
    #include<bits/stdc++.h>
    using namespace std;
    
    #define debug(x) cerr << #x << ": " << (x) << endl
    #define rep(i,a,b) for(int i=(a);i<=(b);i++)
    #define dwn(i,a,b) for(int i=(a);i>=(b);i--)
    #define pb push_back
    #define all(x) (x).begin(), (x).end()
    
    #define x first
    #define y second
    using pii = pair<int, int>;
    using ll = long long;
    
    #define int long long
    
    inline void read(int &x){
        int s=0; x=1;
        char ch=getchar();
        while(ch<'0' || ch>'9') {if(ch=='-')x=-1;ch=getchar();}
        while(ch>='0' && ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
        x*=s;
    }
    
    const int N=1e5+5, M=N<<1;
    
    struct Edge{
    	int to, w, next;
    }e[M];
    
    int h[N], tot;
    
    void add(int u, int v, int w){
    	e[tot].to=v, e[tot].w=w, e[tot].next=h[u], h[u]=tot++;
    }
    
    int n, m;
    vector<int> ring;
    bool vis[N];
    int d[N];
    void dfs(int u){
    	vis[u]=1;
    	for(int i=h[u]; ~i; i=e[i].next){
    		int go=e[i].to;
    		if(!vis[go]) d[go]=d[u]^e[i].w, dfs(go);
    		else ring.pb(d[go]^d[u]^e[i].w);
    	}
    }
    
    signed main(){
    	memset(h, -1, sizeof h);	
    	cin>>n>>m;
    	rep(i,1,m){
    		int u, v, w; read(u), read(v), read(w);
    		add(u, v, w), add(v, u, w);
    	}
    	
    	dfs(1);
    	int res=d[n];
    	vector<int> b;
    	
    	for(auto i: ring){
    		int val=i;
    		for(auto j: b) val=min(val, j^val);
    		if(val) b.pb(val);
    	}
    	
    	for(auto i: b) res=max(res, res^i);
    	
    	cout<<res<<endl;
    	
    	return 0;
    }
    
  • 相关阅读:
    php 学习笔记 数组2
    php 学习笔记 数组1
    jQuery prop 全选和全不全
    jquery获取多个checkbox的值异步提交给php
    php 计算多维数组中所有值的总和
    系统状态码原型
    hadoop win10开发环境
    kafka spring整合版本匹配
    mac 安装brew
    hadoop2.8.5 idea2019.1.3插件安装
  • 原文地址:https://www.cnblogs.com/Tenshi/p/15894629.html
Copyright © 2020-2023  润新知