LOJ #2587. 「APIO2018」铁人两项 (圆方树,树形DP)

开始的时候没有借助圆方树去思考,思路非常混乱,想了很长时间后冷静下来发现这题不就是分类讨论简单题嘛...

题目不难,分两种情况讨论:

设当前点为中间点(圆点) 

  • 起点从一个儿子的子树进入到中间点后进入到另一个儿子的子树. 
  • 起点从一个儿子的子树进入到中间点后仍然回到该儿子子树中(相当于上一条的子问题)    
  • 然后对于儿子(方点)直接累加,父亲(方点)要扣掉当前子树的影响.  

code: 

#include <cstdio>
#include <cstring>
#include <algorithm>  
#include <vector>    
#define N 200006   
#define ll long long 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;      
vector<int>G[N]; 
ll ans;   
int edges,tim,tot,n,m,fr,SIZE;  
int hd[N<<1],to[N<<1],nex[N<<1],dfn[N],low[N],S[N],deg[N],size[N]; 
ll g[N],F[N];     
void add(int u,int v)
{
    nex[++edges]=hd[u],hd[u]=edges,to[edges]=v; 
}
void tarjan(int x)
{
    dfn[x]=low[x]=++tim;     
    S[++fr]=x;   
    ++SIZE; 
    for(int i=hd[x];i;i=nex[i])
    {
        int y=to[i];    
        if(!dfn[y])
        {
            tarjan(y);  
            low[x]=min(low[x],low[y]);   
            if(low[y]>=dfn[x])
            {
                ++tot;        
                G[x].push_back(tot);
                G[tot].push_back(x);  
                ++deg[tot];     
                for(int p=0;p!=y;--fr)
                {
                    p=S[fr];       
                    ++deg[tot];     
                    G[tot].push_back(p);
                    G[p].push_back(tot);     
                }                
            }
        }  
        else 
        {
            low[x]=min(low[x],dfn[y]);              
        }
    }   
}    
void dfs(int x,int ff) 
{
    size[x]=(x<=n);     
    for(int i=0;i<G[x].size();++i) 
    {
        int y=G[x][i];   
        if(y==ff) continue;    
        dfs(y,x);   
        size[x]+=size[y];   
    }
}    
void solve(int x,int ff) 
{    
    if(x<=n) 
    {
        int cu=0; 
        for(int i=0;i<G[x].size();++i) 
        {
            int y=G[x][i];   
            if(y==ff)  cu=SIZE-size[x];     
            else cu=size[y];    
            ans+=1ll*cu*(SIZE-cu-1);       
        }
        if(ff) 
        { 
            ans+=g[ff]-1ll*size[x]*SIZE;      
            ans-=1ll*size[x]*(SIZE-2*size[x]);   
        }
    }            
    else
    { 
        for(int i=0;i<G[x].size();++i) 
        {
            int y=G[x][i]; 
            if(y==ff) continue;      
            F[x]+=1ll*size[y]*(size[x]-size[y]);     
            g[x]+=1ll*size[y]*(SIZE-size[y]);      
        }
        g[x]+=1ll*size[x]*(SIZE-size[x]);                  
        ans+=1ll*F[x];         
    }
    for(int i=0;i<G[x].size();++i) 
        if(G[x][i]!=ff) solve(G[x][i],x); 
}
int main() 
{ 
    // setIO("input");            
    scanf("%d%d",&n,&m);  
    tot=n;    
    for(int i=1;i<=m;++i) 
    {
        int x,y; 
        scanf("%d%d",&x,&y);   
        add(x,y),add(y,x);  
    }
    for(int i=1;i<=n;++i) 
    {
        if(!dfn[i]) 
        {
            tim=SIZE=fr=0; 
            tarjan(i);    
            dfs(i,0);
            solve(i,0);    
        }
    }
    printf("%lld\n",ans);   
    return 0; 
}

  

猜你喜欢

转载自www.cnblogs.com/guangheli/p/12590553.html
今日推荐