1. 程式人生 > >Codeforces Round #430 (Div. 2) C. Ilya And The Tree

Codeforces Round #430 (Div. 2) C. Ilya And The Tree

C. Ilya And The Tree
time limit per test2 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
Ilya is very fond of graphs, especially trees. During his last trip to the forest Ilya found a very interesting tree rooted at vertex 1. There is an integer number written on each vertex of the tree; the number written on vertex i is equal to ai.

Ilya believes that the beauty of the vertex x is the greatest common divisor of all numbers written on the vertices on the path from the root to x, including this vertex itself. In addition, Ilya can change the number in one arbitrary vertex to 0 or leave all vertices unchanged. Now for each vertex Ilya wants to know the maximum possible beauty it can have.

For each vertex the answer must be considered independently.

The beauty of the root equals to number written on it.

Input
First line contains one integer number n — the number of vertices in tree (1 ≤ n ≤ 2·105).

Next line contains n integer numbers ai (1 ≤ i ≤ n, 1 ≤ ai ≤ 2·105).

Each of next n - 1 lines contains two integer numbers x and y (1 ≤ x, y ≤ n, x ≠ y), which means that there is an edge (x, y) in the tree.

Output
Output n numbers separated by spaces, where i-th number equals to maximum possible beauty of vertex i.

Examples
input
2
6 2
1 2
output
6 6
input
3
6 2 3
1 2
1 3
output
6 6 6
input
1
10
output
10

我先說一下我自己的做法,對於一個數組,可以知道它的字首gcd最多隻有log種,由題意可知,只需要列舉拿掉這log種gcd值的第一個,這樣是最優的,原因:因為拿掉一點值後,就相當於原陣列字首gcd和字尾gcd取gcd,字首gcd最多之後log種,那麼只需要對每一種字首gcd值找到它對應的字尾gcd值最大的(字尾越短越好),所以列舉每種字首gcd值得最左邊的那個,用遞迴來寫,線段樹查詢複雜度是:nlogloglog

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+100;
int sum[N<<2];
int n,num[N];
int ans[N];
int que[N];
void update(int x,int c,int l,int r,int rt){
    if(l == r){
        sum[rt] = c;
        return ;
    }
    int mid = (l+r)>>1;
    if(mid >= x) update(x,c,l,mid,rt<<1);
    else update(x,c,mid+1,r,rt<<1|1);
    sum[rt] = __gcd(sum[rt<<1],sum[rt<<1|1]);
}
int query(int L,int R,int l,int r,int rt){
    //cout<<"query" << ' '<< L<< ' '<< R <<endl;
    if(L>R) return 0;
    if(L <= l && R >= r){
        return sum[rt];
    }
    int mid = (l+r)>>1;
    int gd = 0;
    if(mid >= L) gd = query(L,R,l,mid,rt<<1);
    if(mid < R) gd = __gcd(gd,query(L,R,mid+1,r,rt<<1|1));
    return gd;
}
vector<int> G[N];

void dfs(int x,int dep,int f,int cnt,int bef){

    update(dep,num[x],1,n,1);
    //cout <<x << ' '<< bef <<"!!!"<< ' ' << query(1,dep,1,n,1) << endl;
    if(dep == 1) {
        ans[x] = num[x];
        cnt = 1;
        que[1] = 1;
        bef = num[x];

    }
    else{
        int tmp = dep;
        ans[x] = query(1,dep-1,1,n,1);
        if(query(1,dep,1,n,1) != bef){
            cnt ++;
            que[cnt] = dep;
            bef = query(1,dep,1,n,1);
        }
        //cout <<x << ' ' << ans[x] << ' ' << query(1,dep-1,1,n,1) << endl;
        for(int i = 1;i <= cnt;i ++){
            //cout << x << ' '<< i << ' '<< que[i] << endl;
            ans[x] = max(ans[x],__gcd(query(1,que[i]-1,1,n,1),query(que[i]+1,dep,1,n,1)));
        }

    }
    for(int i = 0;i < G[x].size();i ++){
        int v= G[x][i];
        if(v == f)continue;
        dfs(v,dep+1,x,cnt,bef);
    }

}


int main(){
    cin >> n;
    for(int i = 1;i <= n;i ++){
        scanf("%d",&num[i]);
    }
    for(int i = 1;i < n;i ++){
        int u,v;
        scanf("%d %d",&u,&v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs(1,1,-1,0,-1);
    for(int i = 1;i <= n;i ++){
        printf("%d%c",ans[i],i==n?'\n':' ');
    }

    return 0;
}

這種方法還可以通過犧牲空間複雜度優化,把每個特殊點的字首gcd和字尾gcd都存起來,每次加一個值時更新,複雜度是nloglog

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+100;
int sum[N<<2],n,num[N],ans[N],que[N][20];
vector<int> G[N];
void dfs(int x,int dep,int f,int cnt,int bef){
    if(dep == 1) {
        ans[x] = num[x];
        cnt = 1;
        que[1][1] = 0;
        bef = num[x];
    }
    else{
        int tmp = dep;
        ans[x] = bef;
        for(int i = 1;i <= cnt;i ++)
            que[x][i] = __gcd(que[x][i],num[x]);

        if(__gcd(bef,num[x]) != bef){
            cnt ++;
            que[x][cnt] = bef;
            bef = __gcd(bef,num[x]);
        }
        for(int i = 1;i <= cnt;i ++){
            ans[x] = max(ans[x],que[x][i]);
        }
    }
    for(int i = 0;i < G[x].size();i ++){
        int v= G[x][i];
        if(v == f)continue;
        for(int j = 1;j <= cnt;j ++){
            que[v][j] = que[x][j];
        }
        dfs(v,dep+1,x,cnt,bef);
    }
}
int main(){
    cin >> n;
    for(int i = 1;i <= n;i ++){
        scanf("%d",&num[i]);
    }
    for(int i = 1;i < n;i ++){
        int u,v;
        scanf("%d %d",&u,&v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs(1,1,-1,0,-1);
    for(int i = 1;i <= n;i ++){
        printf("%d%c",ans[i],i==n?'\n':' ');
    }

    return 0;
}

然後再介紹一下在status裡看到的nb的做法。
1:
對每個節點開一個set,代表從跟到這個點刪去一個點得到的gcd的值,然後從父親轉移到兒子的就是把父親set裡的值全部跟兒子的值取gcd然後放到兒子的set裡,然後每個節點的答案就是set裡面最大的那個值。
2:
先得到每個節點到根所有點的gcd值,f[n];然後列舉要刪去的節點,然後把這個節點的兒子都跑一遍,當去掉這個點後得到的gcd值和原陣列的gcd值相同時return;然後總體複雜度好像是nlog級別,,,證明我不會,,,