1. 程式人生 > >CF708C 【Centroids】樹上DP

CF708C 【Centroids】樹上DP

洛谷同步部落格

1.我們先來簡化一下問題

如果題目不讓我們改造這棵樹,就很好求了。
先求出 s i z e [ e ] size[e]

(如下圖), e e 為無根樹中的任意一條有向邊(我們在鄰接表中使用一對有向邊 e e e
\overline{e}
來表示,其中 e \overline{e} e
e
的反向邊,表示以 t a i l [ e ] tail[e] (表示邊e的尾)為根的子樹的大小。可知 s i z e [ e ] = n s i z e [ e ] size[\overline{e}]=n-size[e]
圖一
要求出 s i z e [ e ] size[e] s i z e [ e ] size[\overline{e}] ,可以用一個 d f s dfs 來解決( e \overline{e} 可以用一個小技巧:e^1

上程式碼:

void dfs(int u,int e)
{
    size[e]=1;
    for(int i=0;i<(int)G[u].size();i++)
        if(G[u][i]!=(e^1))
        {
            int v=edges[G[u][i]].v;
            dfs1(v,G[u][i]);
            add(u,G[u][i]);
            size[e]+=size[G[u][i]];
        }
    size[e^1]=n-size[e];
}

這樣,就可以了:把每個節點的各個子節點的 s i z e size 值掃描一遍,有 s i z e [ v ] n 2 ( v s o n [ u ] ) size[v]≥\frac{n}{2}(v\in son[u]) 就說明 u u 不是樹的中心。

2.說了半天,回到原問題(還有啊! q w q qwq !)

做好迎接挑戰的準備吧!

我們要把一條邊刪去,並連上另一條新邊,也就是說,要把一個子樹切割下來,在把他連到新的節點上。
我們可以計算一個 c [ e ] c[e] e e 的定義同 s i z e [ e ] size[e] ),表示以 t a i l [ e ] tail[e] 為根的子樹中最多可以割下多少個節點,可知 c [ e ] n 2 c[e] \le \frac{n}{2}
可以在原來的 d f s dfs 的基礎上再加一個 d f s dfs (並用一個數據結構來輔助實現):

void add(int u,int i)
{
    if(best[u]<=c[i])
    {
        sec[u]=best[u];
        best[u]=c[i];
        idx[u]=i;
    }
    else sec[u]=max(sec[u],c[i]);
}
int query(int u,int i)
{
    if(i!=idx[u]) return best[u];
    else return sec[u];
}
void dfs1(int u,int e)
{
    size[e]=1;
    c[e]=0;
    for(int i=0;i<(int)G[u].size();i++)
        if(G[u][i]!=(e^1))
        {
            int v=edges[G[u][i]].v;
            dfs1(v,G[u][i]);
            add(u,G[u][i]);
            size[e]+=size[G[u][i]];
            c[e]=max(c[e],c[G[u][i]]);
        }
    size[e^1]=n-size[e];
    if(size[e]<=n/2) c[e]=size[e];
}
void dfs2(int u,int e)
{
    add(u,e^1);
    for(int i=0;i<(int)G[u].size();i++)
        if(G[u][i]!=(e^1))
        {
            if(size[G[u][i]^1]<=n/2) c[G[u][i]^1]=size[G[u][i]^1];
            else c[G[u][i]^1]=query(u,G[u][i]);
            dfs2(edges[G[u][i]].v,G[u][i]);
        }
}

3.於是我們就大功告成了

獻上我醜陋的程式碼:

#include<iostream>
#include<vector> 
using namespace std;
const int maxn=800005;
struct Edge
{
    int u,v;
    Edge(int x,int y): u(x),v(y){}
};
int n,size[maxn],c[maxn],best[maxn],sec[maxn],idx[maxn];
vector<Edge> edges;
vector<int> G[maxn];
void add(int u,int i)
{
    if(best[u]<=c[i])
    {
        sec[u]=best[u];
        best[u]=c[i];
        idx[u]=i;
    }
    else sec[u]=max(sec[u],c[i]);
}
int query(int u,int i)
{
    if(i!=idx[u]) return best[u];
    else return sec[u];
}
void dfs1(int u,int e)
{
    size[e]=1;
    c[e]=0;
    for(int i=0;i<(int)G[u].size();i++)
        if(G[u][i]!=(e^1))
        {
            int v=edges[G[u][i]].v;
            dfs1(v,G[u][i]);
            add(u,G[u][i]);
            size[e]+=size[G[u][i]];
            c[e]=max(c[e],c[G[u][i]]);
        }
    size[e^1]=n-size[e];
    if(size[e]<=n/2) c[e]=size[e];
}
void dfs2(int u,int e)
{
    add(u,e^1);
    for(int i=0;i<(int)G[u].size();i++)
        if(G[u][i]!=(e^1))
        {
            if(size[G[u][i]^1]<=n/2) c[G[u][i]^1]=size[G[u][i]^1];
            else c[G[u][i]^1]=query(u,G[u][i]);
            dfs2(edges[G[u][i]].v,G[u][i]);
        }
}
int main()
{
    cin>>n;
    for(int i=1;i<n;i++)
    {
        int u,v;
        cin>>u>>v;
        edges.push_back(Edge(u,v));
        edges.push_back(Edge(v,u));
        G[u].push_back(edges.size()-2);
        G[v].push_back(edges.size()-1);
    }
    dfs1(1,edges.size());
    dfs2(1,edges.size());
    for(int i=1;i<=n;i++)
    {
        bool ok=true;
        for(int j=0;j<(int)G[i].size();j++)
            if(size[G[i][j]]-c[G[i][j]]>n/2)
            {
                ok=false;
                break;
            }
        cout<<ok<<' ';
    }
    return 0;
}

P . S . : P.S.: 如有不當,請指出。