hdu多校第九场 1005(hdu-6430:TeaTree)

版权声明:谁他喵没事儿会转我的博客啊QAQ,有没有人看都是一回事儿。。。 https://blog.csdn.net/f2935552941/article/details/82019392

题目大意:

对于一个结点 x,它的值可以是当前子树内任意两点的gcd值。但是要求 lca(i,j)=x。

最后问你每个结点最大的值可能是多少,如果不存在输出-1。

解题思路:

本来想用树上dsu来写的,结果写了一上午,总感觉dsu有哪些地方不对劲,开始删删改改,最后发现写出来了一个暴力合并,复杂度的话不太会证明,可能因为约数较少的缘故,所以复杂度不是很高。

具体实现就是预先打出每个结点的约数,用vector保存起来。保存以后直接dfs,对于当前结点,把它的所有儿子遍历完之后,我们使用一个bitset把当前结点的约数加入bitset中,之后对于它的每个儿子结点,我们在bitset中查询是否存在 对于已经存在的数取max,不存在的话加入bitset 同时加入当前结点的约数表,这样保证我们每次查询的时候都只需要往下查询一层即可。

其实这个做法我后来想想还是能用dsu来优化一部分的,例如我们可以把重儿子的bitset不清空,那么重儿子的bitset就可以加以利用,但是其实我的做法需要把儿子结点的约数表合并到父亲结点,那么还是要遍历重儿子的约数表,那么预先储存的bitset感觉优化就不大了,但是其实也有解决的办法,就是可以先把其他结点的约数合并到重儿子中,最后直接赋值给父亲结点即可。不知道能优化多少,我也不想去写了= = 

Ac代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const int mod=1e9+7;
const int INF=1e9+7;
ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
int n,tmp,ans[maxn];
vector<int> rv[maxn],val[maxn],v[maxn]; //rv保存每个数约数 val保存每个结点约数 v建图
bitset<maxn> vs;
void unite(int x,int y) //将y结点合并进x结点
{
    for(int i=0;i<val[y].size();i++)
    {
        if(vs.test(val[y][i])) tmp=max(tmp,val[y][i]);  //如果bitset中已经存在取max
        else val[x].push_back(val[y][i]),vs.set(val[y][i]); //不存在则加入bitset以及x的约数表
    }
}
void dfs(int x,int fa)
{
    for(int i=0;i<v[x].size();i++)
    {
        int u=v[x][i];
        if(u==fa) continue;
        dfs(u,x);
    }
    tmp=-1;
    for(int i=0;i<val[x].size();i++) vs.set(val[x][i]); //预先将当前结点约数加入bitset
    for(int i=0;i<v[x].size();i++)  //将所有儿子结点的约数加入bitset
    {
        int u=v[x][i];
        if(u==fa) continue;
        unite(x,u);
    }
    ans[x]=tmp;
    vs.reset(); //清空bitset
}
int main()
{
    for(int i=1;i<maxn;i++) //打约数表
    {   
        for(int j=1;j<maxn;j++)
        {
            if(i*j>maxn) break;
            rv[i*j].push_back(i);
        }
    }
    scanf("%d",&n);
    for(int i=2;i<=n;i++)
    {
        int x; scanf("%d",&x);
        v[i].push_back(x);
        v[x].push_back(i);
    }
    for(int i=1;i<=n;i++)   //得到每个结点的约数表
    {
        int x; scanf("%d",&x);
        val[i]=rv[x];
    }
    dfs(1,0);
    for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
    //system("pause");
}

猜你喜欢

转载自blog.csdn.net/f2935552941/article/details/82019392