1. 程式人生 > >101972B B. Updating the Tree

101972B B. Updating the Tree

B. Updating the Tree

time limit per test

1.5 s

memory limit per test

256 MB

input

standard input

output

standard output

A rooted tree is a tree with a countable number of nodes, in which a particular node is distinguished from the others by being the ancestor node of every node of the tree. This node is called the root node.

You are given a rooted tree consisting of n nodes numbered from 1 to n. The root of the tree is node number 1. Each node i has a value viassigned to it.

For each subtree, you must find the minimum number of nodes you must change the value on them to any other value so that the distance between every pair of nodes in that subtree is equal to the absolute difference between the values on them, or say that it is impossible. Can you?

Input

The first line contains an integer T (1 ≤ T ≤ 100) specifying the number of test cases.

The first line of each test case contains an integer n (1 ≤ n ≤ 105), in which n is the number of nodes in the tree. Then a line follow containing n integers v1, ..., vn (1 ≤ vi ≤ 105), in which vi is the value of the i

th node.

Then n - 1 lines follow, each line contains two integers ai and bi (1 ≤ ai, bi ≤ n), giving an edge between nodes ai and bi.

Output

For each test case, print a single line containing n space-separated integers x1, ..., xn, in which xi is the answer of the subtree of node i. If an answer does not exist for a subtree, print  - 1.

Example

input

1
2
1 3
1 2

output

1 0

題意:

給你一棵樹,這棵樹的點都有點權,讓你求對於v為根的子樹改變最小數量點的點權,使得這棵子樹上任意兩個點之間的距離等於點權差的絕對值,1為這整棵樹的根,如果無論怎麼改都無法滿足條件輸出-1。

思路:

當且僅當以v為根的這棵子樹,v這個點只掛不超過2條鏈時,才能有滿足條件的解。因為只有等差數列(一條鏈)且d為1或者-1才滿足條件,超過兩條那麼這整棵子樹無論從哪個角度看都不是一條鏈。

tips:下面掛著兩條鏈的根,根可以看做是從它的葉子結點到另一個葉子結點這條鏈上的一個點。

因此我們考慮找出葉子結點,之後往上爬,一直爬到1或者一個下面掛著兩條鏈的祖先,在往上爬的過程當中我們就已經把對於這條鏈上所有點的答案都統計出來了,往上爬的時候我們會遇到兩種情況:

1.爬到v這個點,v的兒子只有一個,如果v≠1那麼我們可以再接著往上爬。

2.爬到v這個點,v的兒子有兩個,這個時候就需要找下一個葉子結點往上爬,我們需要標記一下v這個點已經爬過了,等到v的另一個葉子結點往上爬遇到v的時候再處理答案。兩條鏈答案的合併直接暴力就行,因為,對於每一個掛著兩條鏈的根,有一條鏈只會多訪問一次。

可以dfs預處理一邊把葉子結點push_back到vector中,並且可以直接記錄出不符合要求的根。

對於怎麼求最少改多少點滿足條件這題問題,首先我們可以設定一個值step,v每爬一次就和v的點權相減得到值x(包括葉子結點),之後step自增(d=1)或者自減(d=-1),x出現次數最多的那一組是不需要動它的,我們維護一個遞增和遞減,答案就是遞增和遞減當中取一個n-xmax。(n是爬的次數,也就是當前v子樹上結點的個數)

上圖舉個例子:

(截圖的時候不小心把小框框截進去了,被蓋掉的部分上面是3,2;下面是0,-1)

仔細觀察會發現step遞增的時候對於x連續相同的一段,val的值也是保持遞增的,這樣就能維護出來了。

dfs是線性的,爬的過程也是線性的,答案合併也是線性的(忽略map),所以時間複雜度O(能過)。

示例程式:

#include <bits/stdc++.h>
#define LDQ 1000000007
#define QAQ 0x3f3f3f3f
#define INF 1000000000000000000
#define inv2 ((LDQ+1)>>1)
const long double PI=3.14159265358979323846;
using namespace std;
struct jj
{
    int v,next;
}w[200000];
int h[100000],ans[100000],pre[100000],vis[100000],val[100000],subtreesize[100000],numw;
vector<int>ve;
map<int,int>mp,mp1;
void insert(int u,int v)
{
    w[numw].v=v;
    w[numw].next=h[u];
    h[u]=numw++;
}
int dfs(int pos,int fa)
{
    int i,num=0,flag=0;
    pre[pos]=fa;                    //pre存父親
    ans[pos]=0;                     //ans存答案
    subtreesize[pos]=1;             //subtreesize存放以v為根子樹上結點的個數
    for(i=h[pos];i!=-1;i=w[i].next)
    {
        if(w[i].v!=fa)
        {
            num++;                          //鏈的個數++
            subtreesize[pos]=subtreesize[pos]+dfs(w[i].v,pos);
            if(ans[w[i].v]==-1||ans[w[i].v]==QAQ-1)//如果有兒子不符合要求,自己也不符合要求,如果它的兒子掛著兩個鏈自己也不滿足要求
            {
                ans[pos]=-1;
            }
        }
    }
    if(num==0)
    {
        ve.push_back(pos);                  //存葉子結點
    }
    if(ans[pos]!=-1)
    {
        if(num>2)                           //鏈的個數大於2的
        {
            ans[pos]=-1;
        }
        else if(num==2)                     //鏈的個數等於2的
        {
            ans[pos]=QAQ-1;
        }
        else                                //它本身或者只有一條鏈的
        {
            ans[pos]=QAQ;
        }
    }
    return subtreesize[pos];
}
int main()
{
    int t,i,i1,n,u,v,temp,step,step1,flag,pos;
    scanf("%d",&t);
    while(t--)
    {
        numw=0;
        ve.clear();
        scanf("%d",&n);
        for(i=0;n>i;i++)
        {
            scanf("%d",&val[i]);
            h[i]=-1;
            vis[i]=0;
        }
        for(i=1;n>i;i++)
        {
            scanf("%d %d",&u,&v);
            insert(u-1,v-1);
            insert(v-1,u-1);
        }
        subtreesize[0]=dfs(0,-1);                       //dfs預處理
        for(i=0;ve.size()>i;i++)
        {
            v=ve[i];
            vis[v]=1;                                   //vis記錄結點有沒有被爬過
            mp.clear();
            mp1.clear();
            temp=0;
            step=step1=0;
            while(ans[v]==QAQ&&v!=-1)                               //只爬一條鏈的情況
            {
                temp=max(temp,++mp[val[v]-step]);                   //用map雜湊一下值
                temp=max(temp,++mp1[val[v]-step1]);                 //遞增遞減兩個中選最大
                step++;
                step1--;
                ans[v]=subtreesize[v]-temp;                         //n-xmax
                vis[v]=1;
                v=pre[v];                                           //往上爬
            }
            if(v!=-1&&ans[v]==QAQ-1)                                //遇到掛著兩條鏈的
            {
                if(vis[v]==1)                                       //如果被爬過就合併答案
                {
                    temp=max(temp,++mp[val[v]-step]);
                    temp=max(temp,++mp1[val[v]-step1]);
                    step++;
                    step1--;
                    pos=v;
                    flag=1;
                    while(flag==1)
                    {
                        flag=0;
                        for(i1=h[v];i1!=-1;i1=w[i1].next)
                        {
                            if(pre[v]!=w[i1].v)                     //因為只有一個兒子我就不開son陣列了
                            {
                                v=w[i1].v;
                                temp=max(temp,++mp[val[v]-step]);
                                temp=max(temp,++mp1[val[v]-step1]);
                                step++;
                                step1--;
                                flag=1;
                                break;
                            }
                        }
                    }
                    ans[pos]=subtreesize[pos]-temp;
                }
                vis[v]=1;
            }
        }
        for(i=0;n>i;i++)
        {
            if(i!=0)
            {
                printf(" ");
            }
            printf("%d",ans[i]);
        }
        printf("\n");
    }
    return 0;
}