zoukankan      html  css  js  c++  java
  • 12 October

    次小生成树

    http://poj.org/problem?id=1679

    不难得出,次小生成树可以由最小生成树更换一条边得到。

    首先构造原图的最小生成树,然后枚举每一条不在最小生成树中的边 (u, v, w),尝试将这条边加入生成树,因为直接加入边会产生环,所以我们需要在加边之前删去最小生成树上 u 到 v 的路径上权值最大的边。在枚举每一条边时我们都会得到一棵生成树,这些生成树中边权和最小的即为要求的次小生成树。

    需要在构造最小生成树时将完整的树结构构造出来,并且使用树上倍增算法查询两点间边权值最大的值。

    强连通分量

    在一个有向图中,如果某两点间都有互相到达的路径,那么称中两个点强连通,如果任意两点都强连通,那么称这个图为强连通图;一个有向图的极大强连通子图称为强连通分量。

    https://oi.men.ci/tarjan-scc-notes/

    https://www.cnblogs.com/stxy-ferryman/p/7779347.html

    一个强连通分量中的点一定处于搜索树中同一棵子树中。

    Tarjan 算法:

    • low[] 表示这个点以及其子孙节点连的所有点中dfn最小的值.
    • s[] 表示当前所有可能能构成是强连通分量的点.
    • col[] 对强连通分量进行染色.
    • v[to[k]]==false 说明无论如何这个点也不能与u构成强连通分量,因为它不能到达u.
    • low[x]==dfn[x] 说明u点及u点之下的所有子节点没有边是指向u的祖先的了,即u点与它的子孙节点构成了一个最大的强连通图即强连通分量.
    • if (!dfn[i]) tarjan(i); Tarjan一遍不能搜完所有的点,因为存在孤立点. 所以我们要对一趟跑下来还没有被访问到的点继续跑Tarjan.

    均摊时间复杂度 (O(n)).

    int dfn[N], low[N], t, s[N], st;
    int col[N], ct;
    bool v[N];
    
    void tarjan(int x) {
        dfn[x]=low[x]=++t, s[++st]=x, v[x]=true;
        for (int k=head[x]; k; k=nex[k]) {
            if (dfn[to[k]]) {if (v[to[k]]) low[x]=min(low[x], dfn[to[k]]); }
            else tarjan(to[k]), low[x]=min(low[x], low[to[k]]);
        }
        if (low[x]==dfn[x]) {
            col[x]=++ct, v[x]=false;
            while (s[st]!=x) col[s[st]]=ct, v[s[st--]]=false;
            --st;
        }
    }
    
    for (int i=1; i<=n; ++i) if (!dfn[i]) tarjan(i);
    

    [USACO06JAN] The Cow Prom:

    for (rint i=1; i<=n; ++i) ++cnt[col[i]];
    for (rint i=1; i<=ct; ++i) if (cnt[i]>1) ++ans;
    printf("%d
    ", ans);
    

    缩点

    缩点: 对于 贡献具有传递性 的题,因为强连通分量中的每两个点都是强连通的,可以将一个强连通分量当做一个 超级点,而点权按题意来定.

    POJ2186 Popular Cows: 告诉你有n头牛,m个崇拜关系,并且崇拜具有传递性,如果a崇拜b,b崇拜c,则a崇拜c,求最后有几头牛被所有牛崇拜.

    显然一个强联通分量内的所有点都是满足条件的,我们可以对整张图进行缩点,然后就简单了.

    剩下的所有点都不是强连通的,现在整张图就是一个DAG(有向无环图).

    那么就变成一道水题了,因为这是一个有向无环图,不存在所有点的出度都不为零的情况.

    所以必然有1个及以上的点出度为零,如果有两个点出度为零,那么这两个点肯定是不相连的,即这两圈牛不是互相崇拜的,于是此时答案为零,如果有1个点出度为0,那么这个点就是被全体牛崇拜的.

    这个点可能是一个强联通分量缩成的 超级点,所以应该输出整个强联通分量中点的个数.

    stxy-ferryman

    /* 以上同 Tarjan 求强连通分量. */
    
    int deg[N], cnt[N];
    int tot=0, ans=0;
    
    for (int i=1; i<=n; ++i) {
        for (int k=head[i]; k; k=nex[k]) if (col[to[k]]!=col[i]) ++deg[col[i]];
        ++cnt[col[i]];
    }
    for (int i=1; i<=ct; ++i) if (!deg[i]) ++tot, ans=cnt[i];
    if (!tot || tot>1) printf("0
    "); else printf("%d
    ", ans); 
    

    割点、桥

    无向图.

    割点

    特判:根节点如果有两个及以上的儿子,那么他也是割点.

    int dfn[N], low[N], t, root;
    bool cut[N];
    
    void tarjan(int x) {
        int flag=0;
        dfn[x]=low[x]=++t;
        for (int k=head[x]; k; k=nex[k]) {
            if (dfn[to[k]]) low[x]=min(low[x], dfn[to[k]]);
            else {
                tarjan(to[k]), low[x]=min(low[x], low[to[k]]);
                if (low[y]>=dfn[x]) {
                    ++flag;
                    if (x!=root || flag>1) cut[x]=true;
                }
            }
        }
    }
    
    for (int i=1; i<=n; ++i) if (!dfn[i]) tarjan(root=i);
    for (int i=1; i<=n; ++i) if (cut[i]) printf("%d ", i);
    

    邻接表存图编号从2开始. 即开头 head[0]=1;.

    int dfn[N], low[N], t;
    bool bdg[N<<1];
    
    void tarjan(int x, int last) {
        dfn[x]=low[x]=++t;
        for (int k=head[x]; k; k=nex[k]) {
            if (dfn[to[k]]) if (i!=(last^1)) low[x]=min(low[x], dfn[to[k]]);
            else {
                tarjan(to[k], k), low[x]=min(low[x], low[to[k]]);
                if (low[y]>=dfn[x]) bdg[k]=bdg[k^1]=true;
            }
        }
    }
    
    for (int i=1; i<=n; ++i) if (!dfn[i]) tarjan(i, 0);
    for (int i=2; i<head[0]; i+=2) if (bdg[i]) printf("%d %d
    ", to[i^1], to[i]);
    

    Euler 函数

    [n={prod_{i=1}^{k}} {p_i}^{a_i} ]

    [varphi(n)=ncdot prod_{i=1}^{k} left(1 - frac{1}{p_i} ight) ]

    int phi() {
        int m = floor(sqrt(n + 0.5)), ans = n;
        for (int i = 2; i <= m; i++) {
            if (n % i == 0) {
                ans = ans / i * (i - 1);
                while (n % i == 0) n /= i;
            }
        }
    
        if (n != 1) ans = ans / n * (n - 1); // 整体为素数
        return ans;
    }
    

    https://oi.men.ci/euler-sieve/

    模意义下的除法

    要求模数为素数。

    Fermat 小定理:

    [a ^ {p - 1} equiv 1 pmod p ]

    [a cdot a ^ {p - 2} equiv 1 pmod p ]

    inline int pow(long long x, int y) {
        long long res=1;
        for (; y; x=x*x%mod, y>>=1) if (y&1) res=res*x%mod;
        return res;
    }
    
    inline int inv(int& x) {return pow(num, mod-2); }
    

    拓展 Euclid 算法:
    在对数时间内求出方程 (ax + by = gcd(a, b)) 的一组解。当 b 为素数时,(gcd(a, b)=1),则

    [ax equiv 1 pmod b ]

    式中 (x) 即为所求。

    void exgcd(int& a, int& b, int &g, int &x, int &y) {
        if (!b) g=a, x=1, y=0;
        else exgcd(b, a%b, g, y, x), y-=x*(a/b);
    }
    
    inline int inv(int& t) {
        register int g, x, y; exgcd(t, mod, g, x, y);
        retuurn ((x%mod)+mod)%mod;
    }
    

    全错位排列递推公式

    [f(n) = (n-1)cdot left[f(n-1)+f(n-2) ight] ]

    常数优化

    书写优化:

    Before After
    x==0 !x
    x!=-1 ~x
    x!=y x^y
    x*10 (x<<3) + (x<<1)
    x*2+1 x<<1|1
    x%2 x&1
    (x+1)%2 x^1
    x%2==0 ~(x&1)

    函数参数尽量取地址。手动内联 inline

    大循环分开来做。register。(手动 cache)

    strlen() 函数提前求好值,避免重复调用。

    表达式合并。(手动并行)

    重载运算符:

    struct mat {
    	static const int ml=10;
    	int m[ml][ml];
    	mat(int x=0) {
    		memset(m, 0, sizeof(m));
            for (int i=0; i<ml; i++) m[i][i]=x;
    	}
    	int* operator [] (int& p) {return m[p]; }
    };
    
  • 相关阅读:
    【Language】 TIOBE Programming Community Index for February 2013
    【diary】good health, good code
    【web】a little bug of cnblog
    【Git】git bush 常用命令
    【web】Baidu zone ,let the world know you
    【diary】help others ,help yourself ,coding is happiness
    【Git】Chinese messy code in widows git log
    【windows】add some font into computer
    SqlServer启动参数配置
    关于sqlserver中xml数据的操作
  • 原文地址:https://www.cnblogs.com/greyqz/p/11664313.html
Copyright © 2011-2022 走看看