zoukankan      html  css  js  c++  java
  • LOJ2587:[APIO2018]铁人两项——题解

    https://loj.ac/problem/2587#submit_code

    (题面来自LOJ)

    考试时候发觉树很可做,并且写了一个dp骗到了树的分。

    苦于不会圆方树……现在回来发现这题还是很可做的!

    先套路套圆方树,然后思考路径条数如何计算。

    一个显然的想法:从一个点双-> 一个点双->……-> 一个点双,条数没准就是每个点双的大小!

    于是我们能够想到方点的权值为点双的大小。

    当然注意到我们选择的起点/终点以及每个点双之间相邻的切点只能走一次,为了去重,我们把圆点权值设为-1。

    则任取起点s,终点t的情况就是s->t的路径上所有点的权值和。

    当然此时我们可以用dp做,但是我当时的代码没拷于是现在我也忘了怎么做了,我们换一种大家普遍(我看其他人代码)的一种方法。

    我们求出来每个点u,有多少条路径通过它即可,具体dp可以看我的代码注释。

    以及注意这题每个结点之间至多有一条,而不是至少!坑死我了。

    #include<cmath>
    #include<queue>
    #include<stack>
    #include<cstdio>
    #include<cctype>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int N=2e5+5;
    const int M=N*2;
    inline int read(){
        int X=0,w=0;char ch=0;
        while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
        while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
        return w?-X:X;
    }
    struct node{
        int u[M],v[M],nxt[M];
        int cnt,head[N];
        void init(){
            cnt=0;
               memset(head,0,sizeof(head));
        }
        void add(int U,int V){
            u[++cnt]=U;v[cnt]=V;nxt[cnt]=head[U];head[U]=cnt;
        }
    }e,g;
    int n,m;
    int dfn[N],low[N],to[N],t,l;
    ll w[N];
    stack<int>q;
    void tarjan(int u,int f){
        dfn[u]=low[u]=++t;
        for(int i=g.head[u];i;i=g.nxt[i]){
            int v=g.v[i];
            if(!dfn[v]){
                q.push(i);
                tarjan(v,u);
                low[u]=min(low[u],low[v]);
                if(low[v]>=dfn[u]){
                    int num;l++;
                    do{
                        num=q.top();q.pop();
                        int uu=g.u[num],vv=g.v[num];
                        if(to[uu]!=l){
                            to[uu]=l;
                            e.add(uu,l+n);e.add(l+n,uu);
                            w[l+n]++;w[uu]=-1;
                        }
                        if(to[vv]!=l){
                            to[vv]=l;
                            e.add(vv,l+n);e.add(l+n,vv);
                            w[l+n]++;w[vv]=-1;
                        }
                    }while(num!=i);
                }
            }else if(low[u]>dfn[v]&&f!=v){
                q.push(i);
                low[u]=dfn[v];
            }
        }
    }
    bool vis[N]; 
    ll ans,size[N],sum;
    void dfs1(int u,int f){
        vis[u]=1;
        size[u]=(u<=n);
        for(int i=e.head[u];i;i=e.nxt[i]){
            int v=e.v[i];
            if(v==f)continue;
            dfs1(v,u);
            size[u]+=size[v];
        }
    }
    void dfs2(int u,int f){
        for(int i=e.head[u];i;i=e.nxt[i]){
            int v=e.v[i];
            if(v==f)continue;
            dfs2(v,u);
            ans+=w[u]*size[v]*(sum-size[v]);
            //以v根树为起点和以v根树补集为终点 
            //已经暗含了当u合法的时候,以u为终点的路径 
        }
        ans+=w[u]*(sum-size[u])*size[u];
        //以u根树补集为起点和以u根树为终点 
        if(u<=n)ans+=w[u]*(sum-1);//以u为起点的路径 
    }
    int main(){
        n=read(),m=read();
        for(int i=1;i<=m;i++){
            int u=read(),v=read();
            g.add(u,v);g.add(v,u);
        }
        for(int i=1;i<=n;i++)
            if(!dfn[i])tarjan(i,0);
        for(int i=1;i<=n;i++){
            if(!vis[i]){
                dfs1(i,0);sum=size[i];dfs2(i,0);
            }
        }
        printf("%lld
    ",ans);
        return 0;
    }

    +++++++++++++++++++++++++++++++++++++++++++

    +本文作者:luyouqi233。               +

    +欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

    +++++++++++++++++++++++++++++++++++++++++++

  • 相关阅读:
    原码、反码、补码详解
    进制转换
    目录
    Window【目录】
    排序算法——冒泡排序
    算法的时间复杂度与空间复杂度
    排序算法
    递归—八皇后问题
    递归—迷宫问题
    递归
  • 原文地址:https://www.cnblogs.com/luyouqi233/p/9074757.html
Copyright © 2011-2022 走看看