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级别,,,证明我不会,,,

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值