AGC010 - C: Cleaning

原題鏈接

題意簡述

給出一棵n(n105) 個節點的樹,每個點有點權。每次可以選擇兩個葉節點並將連接它們的路徑上的節點的點權-1(包括葉節點)。求能否將所有節點的點權都變爲0。

分析

其中v爲葉節點
先考慮最簡單的情況。在這種情況下,au 必須等於av ,否則GG。因爲要想對v操作只能通過u,想對u操作只能通過v。
若相等我們可以令bu=av ,並定義bu 爲:u需要往外連bu 條路徑。因爲需要有au 條路徑進到以u爲根的子樹裏面,可以看做u需要向外連au 條路徑。
其中所有v都是葉節點
再考慮一般情況。在這種情況下,au 必須小於等於bv ,否則GG。因爲即使把bv 都減完了au 也不能爲0,並且已經沒有辦法再減少au 了。
au=bv ,直接從u往外連au 條路徑就好了。
和剛纔不同,v之間可以自行解決一部分。比如操作v1uv2 ,可以讓bu 減1,讓bv 減2。我們可以進行類似的操作直到bu=bv ,然後同上。
但是有可能沒法讓bu=bv ,那就GG。那什麼情況下不可行呢?
結論:max{bv}(bv)/2 時,可以把所有的bv 消成0(或者剩一個1)。

證明

可能不對,看看就好

我們可以把問題反轉一下:
對於一個零序列,每次對兩個位置+1,能否得到目標序列?
顯然的結論有當 max{bv}>(bv)/2 時會有 max{bv}(bvmax{bv}) 加不出來。以及當 bv 爲奇數時至少會剩下一個。
下證對於其他情況:
額外創建兩個位置,對它們進行無限次操作,意思就是足夠多次。
{0,0,...,0} -> {0,0,...,0(,inf,inf)}
這時候我們加入了一種新操作:令這兩個inf 減1,也就是撤回一次。 然後我們可以做到 :
{0,0,...,0(,inf,inf)} -> {1,0,...,0(,inf+1,inf)} -> {2,0,...,0(,inf+1,inf+1)} -> {2,0,...,0(,inf,inf)}
這樣就有了構造方法:先兩兩給所有奇數填上1(要是有奇數個奇數就說明bv 爲奇數肯定會剩下,所以可以把一個奇數視爲偶數),然後通過以上+2的操作把所有數都填好。最後對額外的兩個位置一直-1減到0,這樣就構造完成了。

如果在任何時候出現bu 無法等於bv ,那麼GG。
以及,broot0 也GG。
遍歷所有節點複雜度爲O(n) ,遍歷每個節點的所有子節點複雜度爲O(n) ,總時間複雜度爲O(n)

實現

首先以一個度不爲1的點作爲root ,然後DFS出深度dpt 和樹的結構
由下到上將節點u和它的子節點v合併出bu ,最後檢查 broot

代碼

//Cleaning
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long lint;
int const N=1e5+10;
int n,a[N];
int cnt,h[N],deg[N];
struct edge{
    int u,v,nxt;
    edge(int u1=0,int v1=0)
    {
        u=u1,v=v1;
        nxt=h[u];
        h[u]=cnt;
    }
}ed[N<<1];
int root,fa[N],dpt[N];
struct rec{int dpt,id;} r[N];
bool cmpDpt(rec x,rec y) {return x.dpt>y.dpt;}
void dfs(int u)
{
    for(int i=h[u];i!=0;i=ed[i].nxt)
    {
        int v=ed[i].v;
        if(v==fa[u]) continue;
        fa[v]=u,dpt[v]=dpt[u]+1;
        dfs(v);
    }
}
int main()
{
    freopen("c.in","r",stdin);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    if(n==2)
    {
        if(a[1]!=a[2]) printf("NO");
        else printf("YES");
        return 0;
    }
    cnt=0; memset(h,0,sizeof h);
    for(int i=1;i<=n-1;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        ed[++cnt]=edge(u,v); deg[v]++;
        ed[++cnt]=edge(v,u); deg[u]++;
    }
    for(int i=1;i<=n;i++)
        if(deg[i]>1)
        {
            fa[i]=0,dpt[i]=1;
            dfs(root=i);
            break;
        }
    for(int i=1;i<=n;i++) r[i].dpt=dpt[i],r[i].id=i;
    sort(r+1,r+n+1,cmpDpt);
    for(int i=1;i<=n;i++)
    {
        int u=r[i].id;
        if(deg[u]==1) continue;
        int maxx=0; lint sum=0;
        for(int j=h[u];j!=0;j=ed[j].nxt)
        {
            int v=ed[j].v;
            if(fa[v]!=u) continue;
            maxx=max(maxx,a[v]); sum+=a[v];
        }
        lint in=min(sum/2,sum-maxx);
        if(a[u]>sum) {printf("NO"); return 0;}
        else
        {
            if(sum-a[u]>in) {printf("NO"); return 0;}
            else a[u]-=sum-a[u];
        }
    }
    if(a[root]==0) printf("YES");
    else printf("NO");
    return 0;
}

注意

n=2 時要特判一下,因爲兩個點都是葉節點會找不出root

發佈了42 篇原創文章 · 獲贊 1 · 訪問量 8928
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章