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

 

题目大意:

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

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

 

解题思路:

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

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

 

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

Ac代码:

#include
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 rv[maxn],val[maxn],v[maxn]; //rv保存每个数约数 val保存每个结点约数 v建图
bitset vs;
void unite(int x,int y) //将y结点合并进x结点
{
    for(int i=0;imaxn) 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");
}

 

你可能感兴趣的:(启发式合并)