• [POJ3417]Network


    思路:
    考虑加入新边对原图的影响:
      每加入一条边,相当于在原图中构成一个环,因此要使原图在这个环上断开,必须删去这条新边和环上任意一条树边。
    统计每一条树边出现在多少个环中,计作$c$:
      1.$c=0$,则该边不属于任何一个环,因此删去这条边的同时删去任意一条新边即可,对答案的贡献是$m$;
      2.$c=1$,则该边仅属于一个环,因此删去这条边并删去该环上的新边即可,对答案的贡献是$1$;
      3.$cgeq 2$,则该边同时包含于多个环中,无论删去哪一个新边,总有别的环使原图连通,对答案的贡献是$0$。
    因此我们可以统计$c$来得到答案。
    我们可以利用树上差分的思想,对于每条新边$(u,v)$,$f_u=f_u+1$,$f_v=f_v+1$,$f_{LCA(u,v)}=f_{LCA(u,v)}+1$。
    最后用树形DP求出每条边被环包含的次数。
    Tarjan求LCA。
    但是在OJ上测会TLE,必须要把vector改成前向星才可以。

     1 #include<cstdio>
     2 #include<cctype>
     3 inline int getint() {
     4     char ch;
     5     while(!isdigit(ch=getchar()));
     6     int x=ch^'0';
     7     while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
     8     return x;
     9 }
    10 const int root=1,nil=0;
    11 const int V=100001;
    12 struct Edge {
    13     int to,next;
    14 };
    15 int sz=0;
    16 Edge edge[V<<2];
    17 int e[V]={0},q[V]={0};
    18 inline void add_edge(const int u,const int v) {
    19     edge[++sz]=(Edge){v,e[u]};
    20     e[u]=sz;
    21 }
    22 inline void add_query(const int u,const int v) {
    23     edge[++sz]=(Edge){v,q[u]};
    24     q[u]=sz;
    25 }
    26 class DisjointSet {
    27     private:
    28         int anc[V];
    29     public:
    30         DisjointSet() {
    31             for(int i=0;i<V;i++) anc[i]=i;
    32         }
    33         int Find(const int x) {
    34             return x==anc[x]?x:anc[x]=Find(anc[x]);
    35         }
    36         void Union(const int x,const int y) {
    37             anc[Find(x)]=Find(y);
    38         }
    39 };
    40 DisjointSet s;
    41 bool vis[V]={0};
    42 int f[V]={0};
    43 void Tarjan(const int x,const int par) {
    44     for(int i=e[x];i;i=edge[i].next) {
    45         int &y=edge[i].to;
    46         if(y!=par) Tarjan(y,x);
    47     }
    48     for(int i=q[x];i;i=edge[i].next) {
    49         int &y=edge[i].to;
    50         if(!vis[y]) continue;
    51         int lca=s.Find(y);
    52         f[x]++,f[y]++,f[lca]-=2;
    53     }
    54     s.Union(x,par);
    55     vis[x]=true;
    56 }
    57 int cnt[2]={-1};
    58 void DP(const int x,const int par) {
    59     for(int i=e[x];i;i=edge[i].next) {
    60         int &y=edge[i].to;
    61         if(y==par) continue;
    62         DP(y,x);
    63         f[x]+=f[y];
    64     }
    65     if(f[x]<2) cnt[f[x]]++;
    66 }
    67 int main() {
    68     int n=getint(),m=getint();
    69     for(int i=1;i<n;i++) {
    70         int u=getint(),v=getint();
    71         add_edge(u,v);
    72         add_edge(v,u);
    73     }
    74     for(int i=1;i<=m;i++) {
    75         int u=getint(),v=getint();
    76         add_query(u,v);
    77         add_query(v,u);
    78     }
    79     Tarjan(root,nil);
    80     DP(root,nil);
    81     printf("%d
    ",cnt[0]*m+cnt[1]);
    82     return 0;
    83 }
  • 相关阅读:
    pair
    非整除集合
    集合 set
    实现字通配符*
    vector
    矩阵及其初等变换
    求数组中连续子数组(最少有一个元素)的最大和。
    最长上升序列(Lis)
    st表求区间最大值
    [Noip2015] 信息传递
  • 原文地址:https://www.cnblogs.com/skylee03/p/7435044.html
Copyright © 2020-2023  润新知