給定一棵\(n\)個點的樹,其中若干個點的權值已經給出。如今請爲剩餘點填入一個值,使得相鄰兩個點的差的絕對值剛好爲1。請判斷可否實現,若是能,請將方案一併輸出。
spa
卡了一會,終於想出來了。
首先從深度奇偶性和權值奇偶性這一方面考慮:若是全部已知點的權值與深度的奇偶性關係不全同樣,則必定無解。
而後考慮怎麼構造。若是用已填點將樹分紅若干塊,顯然每一塊是獨立的,如今考慮單獨一塊。
直接想有一點困難,因此咱們先嚐試考慮每個空點\(u\)能填什麼數:考慮這一個塊中的一個有值點,其權值爲\(x\),若是它到\(u\)距離爲\(2k\),那麼\(u\)的填數選擇就有\(x-2k,x-2k+2,...,x,...,x+2k-1,x+2k\);若是它到\(u\)距離爲\(2k+1\),那麼\(u\)的填數選擇就有\(x-2k-1,x-2k+1,x-2k+3...,x+2k-1,x+2k+1\)。
考慮全部的有值點,那麼\(u\)的取值範圍就是這些選擇的交\(S\)。也就是說,只要\(u\)填\(S\)中的權值,單看\(u\)而言就必定能填出知足全部有值點的方案。若\(S\)爲空則全局無解。
但是對於每一個空點,咱們到底選\(S\)中的哪一個權值填入呢?注意到若是隨便填的話,可能會出現跳躍的問題。
我畫了幾個例子。構造例子的方法是先弄一棵填好權值的合法樹,再指定有值位置。當我用上述方法考慮空點的\(S\)時,咱們發現:當且僅當將每個點都取其\(S\)中的最小值時有解(或都取最大值),原問題纔有解,這種填法即一種合法方案;不然無解。
粗略證實:考慮一個點\(u\)的取值集合\(S\),它實際上是一個範圍\([l,r]\),但中間的取值是每隔1取一個的。對於任意一個與\(u\)相鄰的點\(v\),記其權值範圍爲\([l',r']\),則其權值邊界的跨度都不會超過1,即有\(l'=l\pm1\)和\(r'=r \pm1\),注意二者不是互不相關的。爲何?\(S\)記錄的是每個有值點\(x\)到這個點對應的權值範圍的交。走多一步,意味着空隙翻轉(原來是跳一次取一次的),對於走近了的\(x\),其權值範圍以\(x\)爲中心向內空隙翻轉,對於走遠了的\(x\),其權值範圍向外空隙翻轉;也就是一個多了兩端,一個少了兩端。仔細分析下來,\(S\)的邊界變化也不會超過1.
若是有解,那麼這樣填數必定可以知足條件------咱們是貼着邊界走的,而有值點自己也在邊界上。若是這樣填都不能知足,顯然全局無解。
所以咱們對每一個點取\(S\)的最小值,判斷是否合法便可。
至於\(S\)最小值的計算方法,這裏有一個技巧:對於每一個點\(u\),咱們直接維護全部有值點\(x\)對應的範圍的左端點的最大值,即\(x-dis\)的最大值。這樣一來,若是真正意義上\(S\)交集不爲空,那麼這個值就是\(u\)的取值。不然,這個值不管如何都會使得後面的斷定出錯不合法,畢竟取值不知足全部的有值點。
code
#include <cstdio> #include <cstring> using namespace std; const int N=100005; const int INF=1e9; int n,m; int a[N]; int h[N],tot,dep[N]; struct Edge{ int v,next; }e[N*2]; int f[N],g[N],ans[N]; inline int max(int x,int y){ return x>y?x:y; } inline int abs(int x){ return x>=0?x:-x; } void addEdge(int u,int v){ e[++tot]=(Edge){v,h[u]}; h[u]=tot; e[++tot]=(Edge){u,h[v]}; h[v]=tot; } void readData(){ scanf("%d",&n); int u,v; for(int i=1;i<n;i++){ scanf("%d%d",&u,&v); addEdge(u,v); } memset(a,-1,sizeof a); scanf("%d",&m); for(int i=1;i<=m;i++){ scanf("%d%d",&u,&v); a[u]=v; } } void mark_dfs(int u,int fa){ dep[u]=dep[fa]+1; for(int i=h[u],v;i;i=e[i].next) if((v=e[i].v)!=fa) mark_dfs(v,u); } bool firstCheck(){ mark_dfs(1,0); int flag=-1; for(int u=1;u<=n;u++) if(a[u]!=-1){ if(flag==-1) flag=(a[u]^dep[u])&1; else if(((a[u]^dep[u])&1)!=flag) return false; } return true; } void dp_dfs1(int u,int fa){ f[u]=(a[u]!=-1)?a[u]:-INF; for(int i=h[u],v;i;i=e[i].next) if((v=e[i].v)!=fa){ dp_dfs1(v,u); f[u]=max(f[u],f[v]-1); } } bool dp_dfs2(int u,int fa){ if(a[u]!=-1) g[u]=a[u]; ans[u]=(a[u]!=-1)?a[u]:max(f[u],g[u]); if(fa&&abs(ans[u]-ans[fa])!=1) return false; static int ch[N],cnt; static int l[N],r[N]; cnt=0; for(int i=h[u],v;i;i=e[i].next) if((v=e[i].v)!=fa) ch[++cnt]=v; l[0]=g[u]+1; r[cnt+1]=-INF; for(int i=1;i<=cnt;i++) l[i]=max(l[i-1],f[ch[i]]); for(int i=cnt;i>=1;i--) r[i]=max(r[i+1],f[ch[i]]); for(int i=1;i<=cnt;i++) g[ch[i]]=max(l[i-1],r[i+1])-2; for(int i=h[u],v;i;i=e[i].next) if((v=e[i].v)!=fa){ if(dp_dfs2(v,u)==false) return false; if(abs(ans[u]-ans[v])!=1) return false; } return true; } bool solve(){ dp_dfs1(1,0); g[1]=-INF; if(dp_dfs2(1,0)==false) return false; puts("Yes"); for(int i=1;i<=n;i++) printf("%d\n",ans[i]); return true; } int main(){ readData(); if(!firstCheck()||!solve()){ puts("No"); return 0; } return 0; }