APIO2018 铁人两项

APIO2018 铁人两项

题目传送门

题意

比特镇的路网由 \(m\) 条双向道路连接的 \(n\) 个交叉路口组成。
最近,比特镇获得了一场铁人两项锦标赛的主办权。这场比赛共有两段赛程:选手先完成一段长跑赛程,然后骑自行车完成第二段赛程。
比赛的路线要按照如下方法规划:
1、先选择三个两两互不相同的路口\(s\)\(c\)\(f\),分别作为比赛的起点、切换点(运动员在长跑到达这个点后,骑自行车前往终点)、终点。
2、选择一条从\(s\)出发,经过\(c\)最终到达\(f\)的路径。考虑到安全因素,选择的路径经过同一个点至多一次。
在规划路径之前,镇长想请你帮忙计算,总共有多少种不同的选取 \(s\)\(c\)\(f\)的方案,使得在第 2 步中至少能设计出一条满足要求的路径。
\((1 \leq n \leq 100000 , 1 \leq m \leq 200000)\)

题解

考虑题目本质是要求什么,如果我们固定了两个点作为端点,那么答案就是这两个点形成的链所经过的点的个数(不算端点),所以我们所求的就是所有链的链长-2之和。考虑如何化简这个问题,容易想到的是将这个图缩成圆方树,那么我们如何处理圆方树使得能够方便的计算经过点的个数?实际上比较好的一个方法就是将圆方树上的方点的值设为与其相连的原点的数量,每个原点的值设为-1,然后我们现在就把问题转化成了求一棵树上所有路径的点权和,考虑对于每一个点统计贡献,先计算只在以这个点为根的子树中的答案,那么加入一棵子树时,增加的贡献就是当前已加入的子树的大小\(sz[u]\)与待加入的子树的大小\(sz[v]\)的乘积,之后再统计从这个子树出发,到这个子树之外的路径的答案,增加的贡献就是这个子树大小\(sz[u]\)与这整棵树中剩余节点个数的乘积,最后再乘上这个节点的权值,加到答案里即可,总复杂度为\(O(n)\)

Code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
bool Finish_read;
template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('\n');}
template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
/*================Header Template==============*/
#define PAUSE printf("Press Enter key to continue..."); fgetc(stdin);
const int N=3e5+500;
int n,m;
struct Graph {
    int tot;
    int head[N];
    void Init() {tot=0;memset(head,-1,sizeof head);}
    void Addedge(int u,int v) {
        E[++tot].to=v;E[tot].nxt=head[u];head[u]=tot;
        E[++tot].to=u;E[tot].nxt=head[v];head[v]=tot;
    }
    struct edge {
        int to,nxt;
    }E[N<<2];
}G,F;
int dfn[N],low[N],st[N],val[N<<1],sz[N<<1],f[N<<1],vis[N<<1];
int dfnclck,tp,ndcnt,Sz;
ll ans=0;
/*==================Define Area================*/
void Tarjan(int o,int fa) {
    low[o]=dfn[o]=++dfnclck;
    st[++tp]=o;val[o]=-1;Sz++;
    for(int i=G.head[o];~i;i=G.E[i].nxt) {
        int to=G.E[i].to;
        if(to==fa) continue;
        if(!dfn[to]) {
            Tarjan(to,o);
            low[o]=min(low[o],low[to]);
            if(low[to]>=dfn[o]) {
                F.Addedge(o,++ndcnt);val[ndcnt]=1;
                int nw=0;
                do {
                    nw=st[tp];tp--;val[ndcnt]++;
                    F.Addedge(ndcnt,nw);
                }while(nw!=to);
            }
        }
        else low[o]=min(low[o],dfn[to]);
    }
}

void Calc(int o,int fa) {
    sz[o]=(o<=n);
    ll cnt=0;
    for(int i=F.head[o];~i;i=F.E[i].nxt) {
        int to=F.E[i].to;
        if(to==fa) continue ;
        Calc(to,o);
        cnt+=2ll*sz[o]*sz[to];
        sz[o]+=sz[to];
    }
    cnt+=2ll*sz[o]*(Sz-sz[o]);
    ans+=cnt*val[o];
}

int main() {
    read(n);read(m);
    G.Init();F.Init();
    for(int i=1,u,v;i<=m;i++) {
        read(u);read(v);
        G.Addedge(u,v);
    }
    ndcnt=n;
    for(int i=1;i<=n;i++) if(!dfn[i]) Sz=0,Tarjan(i,0),Calc(i,0);
    printf("%lld\n",ans);
    return 0;
}
posted @ 2018-11-06 15:34  Apocrypha  阅读(370)  评论(0编辑  收藏  举报