【ARC063E】Integers on a tree

Description

  
   給定一棵\(n\)個點的樹,其中若干個點的權值已經給出。如今請爲剩餘點填入一個值,使得相鄰兩個點的差的絕對值剛好爲1。請判斷可否實現,若是能,請將方案一併輸出。
  
  
  spa

Solution

  
   卡了一會,終於想出來了。
  
   首先從深度奇偶性和權值奇偶性這一方面考慮:若是全部已知點的權值與深度的奇偶性關係不全同樣,則必定無解。
  
   而後考慮怎麼構造。若是用已填點將樹分紅若干塊,顯然每一塊是獨立的,如今考慮單獨一塊。
  
   直接想有一點困難,因此咱們先嚐試考慮每個空點\(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

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;
}
相關文章
相關標籤/搜索