zoukankan      html  css  js  c++  java
  • HDU 5544 Ba Gua Zhen ( 2015 CCPC 南阳 C、DFS+时间戳搜独立回路、线性基 )

    题目链接

    题意 : 给出一副简单图、要你找出一个回路、使得其路径上边权的异或和最大

    分析 :

    类似的题有 BZOJ 2115

    对于这种异或最长路的题目(走过的边可以重复走)

    答案必定是由一条简单路径(链) + 一些基本环构成

    这是因为操作是 xor , 具有自反性质 , 可能需要脑补一下

    回到这题, 发现答案就是要求找出一个环

    那么根据上面那道题目的启发

    答案是一个环的情况下, 那么答案环必定也是由其他环来组成

    那么只要找出图中所有的基本环, 就可以由这些基本环来线性组合出最大 xor 环了

    有一个定理

    对于一个图而言、其独立回路的个数为 M - N + 1

    独立回路是指任意一个都不能由其他回路构成。

    引用一段数学归纳法证明:

    “M=N-1时,树,结论成立

    设M=K时结论成立,当M=K+1时,任取G中一条边e,G-e中有K-N+1个独立回路,且

    任取一个包含e的回路C,显然独立于之前的回路

    任意两个包含e的回路C1与C2,C12=C1+C2是G-e的回路,C2不独立

    故能且仅能增加一个包含e的独立回路

    从而G中恰有(K+1)-N+1个独立回路,证毕”

    红色字体引用自 ==> Click here

    即一个图最多只有 M + ( N - 1 ) 个独立回路 (即基本环)

     

    而除开独立回路外的图中剩余所有回路都能被这 M - N + 1 个独立回路线性表示

    那么由于异或拥有自反性质、即走过两遍的路径不会产生贡献

    接下来如果能找出所有的独立回路、这样问题就变成了、给出 N 个数

    从中找出异或和最大的组合、这个可以用线性基轻松做到

    如何用DFS找出图中所有的独立回路?

    首先你考虑生成树, 对于一个图的生成树而言

    其基本环(不能由其他环线性表示的环)就是任意两个树上节点 + 非树边构成

    那么先 DFS 出生成树, 加上时间戳, 若干当前节点的时间戳大于被遍历到的节点的时间戳

    则说明找到一个上面那样子的非树边, 即找到了一个基本环

    这样子找出来的环正好有 M - ( N - 1 ) 即所有的边 - 树边这么多

    参考自 ==> Click here , Click here

    #include<bits/stdc++.h>
    #define LL long long
    #define ULL unsigned long long
    
    #define scl(i) scanf("%lld", &i)
    #define scll(i, j) scanf("%lld %lld", &i, &j)
    #define sclll(i, j, k) scanf("%lld %lld %lld", &i, &j, &k)
    #define scllll(i, j, k, l) scanf("%lld %lld %lld %lld", &i, &j, &k, &l)
    
    #define scs(i) scanf("%s", i)
    #define sci(i) scanf("%d", &i)
    #define scd(i) scanf("%lf", &i)
    #define scIl(i) scanf("%I64d", &i)
    #define scii(i, j) scanf("%d %d", &i, &j)
    #define scdd(i, j) scanf("%lf %lf", &i, &j)
    #define scIll(i, j) scanf("%I64d %I64d", &i, &j)
    #define sciii(i, j, k) scanf("%d %d %d", &i, &j, &k)
    #define scddd(i, j, k) scanf("%lf %lf %lf", &i, &j, &k)
    #define scIlll(i, j, k) scanf("%I64d %I64d %I64d", &i, &j, &k)
    #define sciiii(i, j, k, l) scanf("%d %d %d %d", &i, &j, &k, &l)
    #define scdddd(i, j, k, l) scanf("%lf %lf %lf %lf", &i, &j, &k, &l)
    #define scIllll(i, j, k, l) scanf("%I64d %I64d %I64d %I64d", &i, &j, &k, &l)
    
    #define lson l, m, rt<<1
    #define rson m+1, r, rt<<1|1
    #define lowbit(i) (i & (-i))
    #define mem(i, j) memset(i, j, sizeof(i))
    
    #define fir first
    #define sec second
    #define VI vector<int>
    #define ins(i) insert(i)
    #define pb(i) push_back(i)
    #define pii pair<int, int>
    #define VL vector<long long>
    #define mk(i, j) make_pair(i, j)
    #define all(i) i.begin(), i.end()
    #define pll pair<long long, long long>
    
    #define _TIME 0
    #define _INPUT 0
    #define _OUTPUT 0
    clock_t START, END;
    void __stTIME();
    void __enTIME();
    void __IOPUT();
    using namespace std;
    
    const int maxn = 5e4 + 10;
    
    struct EDGE{ int v, nxt; LL w; }Edge[maxn<<2];
    
    int Head[maxn], EdgeCnt = 0;
    
    int n, m;
    
    inline void EdgeInit()
    {
        mem(Head, -1);
        EdgeCnt = 0;
    }
    
    inline void AddEdge(int from, int to, LL weight)
    {
        int & cnt = EdgeCnt;
        Edge[cnt].v = to;
        Edge[cnt].w = weight;
        Edge[cnt].nxt = Head[from];
        Head[from] = cnt++;
    }
    
    struct L_B {
        LL d[65], p[65];
        int cnt;
        void init() {
            memset(d, 0, sizeof(d));
            memset(p, 0, sizeof(p));
            cnt = 0;
        }  // 1e18以内的数都适用.
        bool Insert(LL val) {
            for (int i = 63 ; i >= 0 ; i --) {
                if (val & (1ll << i)) {
                    if (!d[i]) {
                        d[i]=val;
                        break;
                    }
                    val^=d[i];
                }
            }
            return val > 0;
            // 可判断val是否存在于线性基当中.
        }
        LL query_max() {
            LL res = 0;
            for (int i = 63 ; i >= 0 ; i --) {
                if ((res^d[i]) > res) res ^= d[i];
            }
            return res;
        }
        LL query_min() {  // 应该预先判断能否是0的情况..QAQ
            for (int i = 0 ; i <= 60 ; i ++) {
                if (d[i]) return d[i];
            }
            return 0;
        }
        void rebuild() { // 用于求第k小值.需要先进行独立预处理
            for (int i = 60 ; i >= 0 ; i --) {
                for (int j = i-1 ; j >= 0 ; j --) {
                    if (d[i] & (1ll<<j)) d[i] ^= d[j];
                }
            }
            for (int i = 0 ; i <= 60 ; i ++) {
                if (d[i]) p[cnt++]=d[i];
            }
        }
        LL kthquery(LL k) { // 注意判断原序列异或出0的情况, 此时应该将k -- 在进行后面的操作.
            LL res = 0;
            if (k >= (1ll << cnt)) return -1;
            for (int i = 60 ; i >= 0 ; i --) {
                if (k & (1LL<<i)) res ^= p[i];
            }
            return res;
        }
        void Merge(const L_B &b) { // 把b这个线性基插入到当前这个线性基中.
            for (int i = 60 ; i >= 0 ; i --)
                if (b.d[i]) Insert(b.d[i]);
        }
    }LB;
    
    int DFN[maxn];
    int DFS_Clocks;
    LL val[maxn];
    VL CycleVal;
    
    inline void DFS(int u, int fa)
    {
        DFN[u] = ++DFS_Clocks;
        vis[u] = true;
        for(int i=Head[u]; ~i; i=Edge[i].nxt){
            int v = Edge[i].v;
            if(v == fa) continue;
            if(vis[v] && DFN[v] < DFS_Clocks){///若当前当前点的时间戳大于出度点、则说明找到一条回路
                CycleVal.pb(val[v] ^ val[u] ^ Edge[i].w);
            }else if(!vis[v]){
                val[v] = val[u] ^ Edge[i].w;
                DFS(v, u);
            }
        }
    }
    
    
    
    
    int main(void){__stTIME();__IOPUT();
    
    
        int nCase;
        sci(nCase);
        for(int Case=1; Case<=nCase; Case++){
    
            scii(n, m);
    
            EdgeInit();
    
            for(int i=1; i<=m; i++){
                int u, v;
                scii(u, v);
                LL w;
                scl(w);
                AddEdge(u, v, w);
                AddEdge(v, u, w);
            }
    
            CycleVal.clear();
            for(int i=0; i<=n; i++)
                vis[i] = false,
                val[i] = 0LL;
            DFS_Clocks = 0;
            for(int i=1; i<=n; i++)
                if(!vis[i])
                    DFS(i, -1);
    
            LB.init();
    
            for(int i=0; i<(int)CycleVal.size(); i++)
                LB.Insert(CycleVal[i]);
    
            printf("Case #%d: %lld
    ", Case, LB.query_max());
        }
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    __enTIME();return 0;}
    
    
    void __stTIME()
    {
        #if _TIME
            START = clock();
        #endif
    }
    
    void __enTIME()
    {
        #if _TIME
            END = clock();
            cerr<<"execute time = "<<(double)(END-START)/CLOCKS_PER_SEC<<endl;
        #endif
    }
    
    void __IOPUT()
    {
        #if _INPUT
            freopen("in.txt", "r", stdin);
        #endif
        #if _OUTPUT
            freopen("out.txt", "w", stdout);
        #endif
    }
    View Code

    顺带一提, 找出图中所有的简单环, 这个东西是 NP 的, 正是由于 xor

    有自反这种美妙的性质, 才能通过基本环+线性基来做这题

    求出图中所有环数量的算法, 也是考虑通过生成树的方法 ==> Click here 

     Codeforces 也有过一道题目, 要求找出环的数量, 标算是状压 ==> Click here

  • 相关阅读:
    为ccflow增加禁用用户立刻生效功能
    关于工作流引擎授权问题的需求变更
    sql server 2005 安装过程中出现错误Insatalling performance countter: Cannot create a file when that file already exists.
    web.config中错误
    三个SQL视图查出所有SQL Server数据库字典
    恢复备份的数据库
    sql语句读取excel数据
    It is an error to use a section registered allowDefinition='MachineToApplication' beyond application level. 错误
    DOS命令实现创建文件夹
    如何查看sql server 的版本(网摘)
  • 原文地址:https://www.cnblogs.com/qwertiLH/p/9758095.html
Copyright © 2011-2022 走看看