zoukankan      html  css  js  c++  java
  • P4630-[APIO2018]Duathlon铁人两项【圆方树】

    正题

    题目链接:https://www.luogu.com.cn/problem/P4630


    题目大意

    (n)个点(m)条边的一张无向图,求有多少对三元组((s,c,f))满足(s eq f eq t)且存在一条从(s)(f)的简单路径经过(c)


    解题思路

    一个比较显然的结论是在一个点双中的三个点((a,b,c))那么必然存在一条(a)(b)的简单路径经过(c)。因为一定存在两条不交的(a->c)(c->b)的路径,那么如果一条(a->c)(c->b)的路径交了,那么另一条就一定不交。

    然后从一个点双出来后就不能再回到这个点双了,所以我们可以考虑在圆方树上做这个问题。

    设定义圆点的权值为(-1),方点的权值为连接的圆点数量,这样我们在圆方树上走的时候就可以固定经过进入和离开这个点双的点了。

    然后问题就变为了求每条圆点之间路径的点权和的和。

    用树形(dp)搞就好了,时间复杂度(O(n))


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #include<stack>
    using namespace std;
    const int N=2e5+10;
    int n,m,num,cnt,dfc,w[N];
    int low[N],dfn[N],siz[N];
    vector<int> G[N],T[N];
    stack<int> s;
    long long ans;
    void tarjan(int x){
        dfn[x]=low[x]=++dfc;
        w[x]=-1;s.push(x);num++;
        for(int y:T[x])
            if(!dfn[y]){
                tarjan(y);
                low[x]=min(low[x],low[y]);
                if(dfn[x]==low[y]){
                    ++cnt;int k;
                    do{
                        k=s.top();
                        G[cnt].push_back(k);
                        G[k].push_back(cnt);
                        w[cnt]++;s.pop();
                    }while(k!=y);
                    G[cnt].push_back(x);
                    G[x].push_back(cnt);
                    w[cnt]++;
                }
            }
            else low[x]=min(low[x],dfn[y]);
        return;
    }
    void solve(int x,int fa){
        siz[x]=(x<=n);
        for(int y:G[x]){
            if(y==fa)continue;
            solve(y,x);
            ans+=2ll*siz[y]*siz[x]*w[x];
            siz[x]+=siz[y];
        }
        ans+=2ll*siz[x]*(num-siz[x])*w[x];
        return;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++){
            int x,y;scanf("%d%d",&x,&y);
            T[x].push_back(y);
            T[y].push_back(x);
        }
        cnt=n;
        for(int i=1;i<=n;i++)
            if(!dfn[i]){
                num=0;
                tarjan(i);
                solve(i,0);
            }
        printf("%lld
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    LR杂记-nmon+analyser监控linux系统资源
    accept函数
    android performClick使用
    #line 的作用是改变当前行数和文件名称
    C++常用排序法、随机数
    C语言运算符优先级及结合性
    如何高效把一字节的位对换, bit0和bit7,bit1和bit6,以此类推.
    NumPy
    Rational Rose、PowerDesign、Visio的一些比较
    vld,Bounds Checker,memwatch,mtrace,valgrind,debug_new几种内存泄露检测工具的比较,Valgrind Cheatsheet
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/14273423.html
Copyright © 2011-2022 走看看