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\n",ans);
    return 0;
}

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

+本文作者:luyouqi233。               +

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

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

猜你喜欢

转载自www.cnblogs.com/luyouqi233/p/9074757.html