hdu 6430 TeaTree 线段树合并

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6430

对每个节点开一颗线段树,标程的写法很节省空间:预处理出每个数字的因数,对题目中给定的权值建线段树,如果子树区间中没有根节点权值的因数,那么直接剪掉。然后通过改变每个根节点左右孩子数组的值,来改变其指向的子树根节点(即合并过程)。相当于是一棵精简的线段树。

因为权值最大为1e5,所以nlognlogn不会T。

合并时对每一对父子节点都尽可能地找到底(s==t)如果能到达底部,说明父子节点都有这个因数,s(或者t)就可能是gcd的最大值,将其与ans值比较更新。而因为每一个节点在以其为根的子树中总是最后被遍历的,所以在此过程中就符合了LCA的要求。

#include
#define inf 0x3f3f3f3f
#define mod 1000000007
#define For(i,m,n) for(int i=m;i<=n;i++)
#define Dor(i,m,n) for(int i=m;i>=n;i--)
#define LL long long
#define lan(a,b) memset(a,b,sizeof(a))
#define sqr(a) a*a
using namespace std;


const int maxn=100005;
int tot;

vector d[maxn];
int ls[maxn*400],rs[maxn*400];
int ans[maxn],f[maxn],root[maxn];
int fa;
void init()
{
    For(i,1,maxn-5)
        for(int j=1;j*i<=maxn-5;j++)
            d[j*i].push_back(i);
}

void build(int& x,int s,int t,int pos)
{
    if(!x){x=++tot;ls[x]=rs[x]=0;}
    if(s==t)return;
    int mid=(s+t)>>1;
    if(pos>mid)build(rs[x],mid+1,t,pos);
    else build(ls[x],s,mid,pos);
}

int merge(int x,int y,int s,int t)
{
    if(!x||!y) return x^y;
    if(s==t){ans[fa]=max(ans[fa],s);return x;}
    int mid=(s+t)>>1;
    ls[x]=merge(ls[x],ls[y],s,mid);
    rs[x]=merge(rs[x],rs[y],mid+1,t);
    return x;
}

int main()
{
    int n;
    init();
    while(~scanf("%d",&n))
    {
        lan(ans,-1);
        For(i,2,n)
            scanf("%d",&f[i]);
            int x;
        tot=0;
        For(i,1,n)
        {
            scanf("%d",&x);
            root[i]=++tot;
            ls[tot]=rs[tot]=0;
            For(j,0,d[x].size()-1)
                build(root[i],1,1e5,d[x][j]);
        }
        Dor(i,n,1)
        {
            fa=f[i];
            root[fa]=merge(root[fa],root[i],1,1e5);
        }
        For(i,1,n)
            printf("%d\n",ans[i]);
    }
    return 0;
}

 

你可能感兴趣的:(线段树合并)