• 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]; }
    };
    
  • 相关阅读:
    jquery flot详解
    AngularJS例子 ng-repeat遍历输出
    JS正则表达式
    jQuery Validate验证框架详解
    解决IE6下png图片不透明
    IT经理,你在这个位置吗
    如何做一个好的前端重构工程师
    noi1816 画家问题(技巧搜索Dfs)
    codevs1380 没有丧尸的舞会
    2016年春季大学先修课考试
  • 原文地址:https://www.cnblogs.com/greyqz/p/11664313.html
Copyright © 2020-2023  润新知