hdu3804(树链剖分+线段树)

题目:http://acm.hdu.edu.cn/showproblem.php?pid=3804

题意:给你一个树,每条边都有一个权值,现在有m组询问,每次询问输入一个二元组(x,y),其结果要满足以下两个条件的最大值

1、必须是从x->1路径上的一条边

2该边的权值必须<=y,且是最大的边

分析:把询问离线处理,先把原本树的边按权值从小到大排序,询问按y值从小到大排,对于边的权值可看作点的权值,每条边的权值都作为儿子结点的权值,根结点的权值赋为-1。

按照询问每次把<=y的边插入线段树,线段树维护区间最大值,再询问最大值,当然在树上维护线段树要树链剖分。

Ac code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
int head[maxn],tot,n;
struct E{
    int u,v,w;
};
vector<E>e;
struct Edge
{
    int v,w,nxt;
} edge[maxn<<1];
int tree[maxn<<2];
int sze[maxn],fa[maxn],son[maxn],dfn[maxn],dep[maxn],rk[maxn],cnt,top[maxn];
void init()
{
    e.clear();
    memset(edge,0,sizeof edge);
    tot=cnt=0;
    memset(sze,0,sizeof sze);
    memset(fa,0,sizeof fa);
    memset(son,0,sizeof son);
    memset(dfn,0,sizeof dfn);
    memset(dep,0,sizeof dep);
    dep[1]=1;
    memset(rk,0,sizeof rk);
    memset(head,-1,sizeof head);
}
struct Node
{
    int x,y,id,ans;
} node[maxn];
bool cmp1(const Node& a,const Node& b)
{
    return a.y<b.y;
}
bool cmp2(const Node& a,const Node& b)
{
    return a.id<b.id;
}
void addedge(int u,int v,int w)
{
    edge[tot].v=v;
    edge[tot].w=w;
    edge[tot].nxt=head[u];
    head[u]=tot++;
}
void pushup(int rt)
{
    tree[rt]=max(tree[rt<<1],tree[rt<<1|1]);
}
void buildtree(int rt,int l,int r)
{
    if(l==r)
    {
        tree[rt]=-1;
        return;
    }
    int mid=(l+r)>>1;
    buildtree(rt<<1,l,mid);
    buildtree(rt<<1|1,mid+1,r);
    pushup(rt);
}
void update(int rt,int l,int r,int p,int val)///单点更新,不需要树剖
{
    if(l==r)
    {
        tree[rt]=val;
        return;
    }
    int mid=(l+r)>>1;
    if(p<=mid) update(rt<<1,l,mid,p,val);
    else update(rt<<1|1,mid+1,r,p,val);
    pushup(rt);
}
int query(int rt,int l,int r,int L,int R)
{
    if(L<=l&&r<=R)
    {
        return tree[rt];
    }
    int ans=-1;
    int mid=(l+r)>>1;
    if(mid>=L) ans=max(ans,query(rt<<1,l,mid,L,R));
    if(mid<R) ans=max(ans,query(rt<<1|1,mid+1,r,L,R));
    return ans;
}
void dfs1(int u,int faa)
{
    sze[u]=1;
    for(int i=head[u]; ~i; i=edge[i].nxt)
    {
        int v=edge[i].v;
        if(v==faa) continue;
        fa[v]=u;
        dep[v]=dep[u]+1;
        dfs1(v,u);
        sze[u]+=sze[v];
        if(sze[v]>sze[son[u]]) son[u]=v;
    }
}
void dfs2(int u,int Top)
{
    dfn[u]=++cnt;
    rk[cnt]=u;
    top[u]=Top;
    if(son[u]) dfs2(son[u],Top);
    for(int i=head[u]; ~i; i=edge[i].nxt)
    {
        int v=edge[i].v;
        if(v!=fa[u]&&v!=son[u])
            dfs2(v,v);
    }
}
int qrange(int u)///区间更新,更新u->1这条链,需要树剖
{
    int v=1,ans=-1;
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        ans=max(ans,query(1,1,n,dfn[top[u]],dfn[u]));
        u=fa[top[u]];
    }
    if(dep[u]<dep[v]) swap(u,v);
    ans=max(ans,query(1,1,n,dfn[v],dfn[u]));
    return ans;
}
bool cmp3(const E& a ,const E& b)
{
    return a.w<b.w;
}
int main()
{
    int t;
    scanf("%d",&t);
    int  u,v,w,m;
    while(t--)
    {
        scanf("%d",&n);
        init();
        for(int i=1; i<n; i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
            addedge(v,u,w);
            e.push_back({u,v,w});
        }
        dfs1(1,0);
        dfs2(1,1);
        buildtree(1,1,n);
        scanf("%d",&m);
        for(int i=1; i<=m; i++)
        {
            scanf("%d%d",&node[i].x,&node[i].y);
            node[i].id=i;
        }
        sort(e.begin(),e.end(),cmp3);
        sort(node+1,node+m+1,cmp1);
        int j=0;
        for(int i=1; i<=m; i++)
        {
            int x=node[i].x,y=node[i].y;
            while(j<n-1){
                if(e[j].w>node[i].y) break;
                int c;
                if(dep[e[j].u]>dep[e[j].v])///选更深的一个点u,更新u->1这条链上的最大值
                    c=e[j].u;
                else
                    c=e[j].v;
                update(1,1,n,dfn[c],e[j].w);
                j++;
            }
            node[i].ans=qrange(x);
        }
        sort(node+1,node+m+1,cmp2);
        for(int i=1;i<=m;i++)
            printf("%d\n",node[i].ans);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值