【BZOJ4009】[HNOI2015]接水果

题目描述

给你一颗有 n 个节点的树,给你p条路径作为盘子,再给你 q 条路径作为水果,问被当前水果完全覆盖的第k大盘子的权值是多少。

题目解析

之前有道题和这个特别相似,把路径 (x,y) 通过端点的 dfn 转化为点对 (dfnx,dfny) ,然后在线段树上进行插入和查询,因为是考试,所以说我一下子就兴奋了,这不就是那道题的翻版吗,把线段树套线段树改成主席树套线段树就行了,于是怒敲代码,然后Wa了。后面才发现自己太单纯了,把盘子转化成点插入,然后查询水果的区间,然而并么有注意到水果中盘子的区间很有可能是不连续的,GG。
好,开始说正解,还是将路径转化为点,然后我们可以发现,如果直接将盘子插入,水果区间查询是查不出来的,盘子包含在水果 (u,v) 内,就是左端点在水果内加上右端点在水果内,然而我们并不能知道水果所代表的区间,也许你会说是 (u>lca(u,v))+(lca(u,v)>v) ,但是这两条路径上的 dfn 不一定是连续的。
故,我们将盘子区间插入,水果单点查询,我们可以知道盘子所能接住的水果的区间:

  1. u v是父子关系时(设 depu<depv ),水果左端点就在 u 点子树中,右端点在除v>u子树外的其他所有位置。
  2. u v处于 lca(u,v) 的不同子树内,那么左右端点分别在 u 点和v点的子树中。
    (具体的范围就用dfs序实现)

代码

/**************************************************************
    Problem: 4009
    User: bzjudge2
    Language: C++
    Result: Accepted
    Time:10044 ms
    Memory:518264 kb
****************************************************************/

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<vector>
using namespace std;

#define MAXN 40000
#define MAXP 40000
#define MAXQ 40000
#define MAXLOG 16
#define INF 0x3f3f3f3f
typedef long long int LL;

template<class T>
void Read(T &x){
    x=0;char c=getchar();bool flag=0;
    while(c<'0'||'9'<c){if(c=='-')flag=1;c=getchar();}
    while('0'<=c&&c<='9'){x=x*10+c-'0';c=getchar();}
    if(flag)x=-x;
}

int n,p,q,maxlog;

struct leaf{
    int tag;
    int ls,rs;
}Leaf[MAXN*MAXLOG*MAXLOG*4+100];
int Cntleaf=0;
int newleaf(){
    int x=++Cntleaf;
    Leaf[x].tag=Leaf[x].ls=Leaf[x].rs=0;
    return x;
}

void insert_leaf(int l,int r,int ll,int rr,int last,int &now){
    now=newleaf();
    Leaf[now]=Leaf[last];
    if(l<=ll&&rr<=r){
        ++Leaf[now].tag;
        return;
    }
    else{
        int mid=(ll+rr)>>1;
        if(r<=mid)insert_leaf(l,r,ll,mid,Leaf[now].ls,Leaf[now].ls);
        else if(mid<l)insert_leaf(l,r,mid+1,rr,Leaf[now].rs,Leaf[now].rs);
        else{
            insert_leaf(l,mid,ll,mid,Leaf[now].ls,Leaf[now].ls);
            insert_leaf(mid+1,r,mid+1,rr,Leaf[now].rs,Leaf[now].rs);
        }
    }
}
int query_leaf(int pos,int ll,int rr,int now){
    if(!now)return 0;
    if(ll==rr)return Leaf[now].tag;
    int mid=(ll+rr)>>1;
    if(pos<=mid)return Leaf[now].tag+query_leaf(pos,ll,mid,Leaf[now].ls);
    else return Leaf[now].tag+query_leaf(pos,mid+1,rr,Leaf[now].rs);
}

struct line{
    int rtid;
    int ls,rs;
}Line[MAXN*MAXLOG*4];
int rt[MAXN+10];
int LineCnt=0;
int newline(){
    int x=++LineCnt;
    Line[x].rtid=Line[x].ls=Line[x].rs=0;
    return x;
}

void insert_line(int l,int r,int ly,int ry,int ll,int rr,int last,int &now){
    now=newline();
    Line[now]=Line[last];
    if(l<=ll&&rr<=r){
        insert_leaf(ly,ry,1,n,Line[now].rtid,Line[now].rtid);
        return;
    }
    else{
        int mid=(ll+rr)>>1;
        if(r<=mid)insert_line(l,r,ly,ry,ll,mid,Line[now].ls,Line[now].ls);
        else if(mid<l)insert_line(l,r,ly,ry,mid+1,rr,Line[now].rs,Line[now].rs);
        else{
            insert_line(l,mid,ly,ry,ll,mid,Line[now].ls,Line[now].ls);
            insert_line(mid+1,r,ly,ry,mid+1,rr,Line[now].rs,Line[now].rs);
        }
    }
}

int query_line(int pos,int y,int ll,int rr,int now){
    if(!now)return 0;
    int rn=query_leaf(y,1,n,Line[now].rtid);
    if(ll==rr)return rn;
    int mid=(ll+rr)>>1;
    if(pos<=mid)return rn+query_line(pos,y,ll,mid,Line[now].ls);
    else return rn+query_line(pos,y,mid+1,rr,Line[now].rs);
}

struct node{
    int v;
    node *nxt;
}*adj[MAXN+10],Edges[MAXN*2+10],*New=Edges;
void addedges(int u,int v){
    node *p=++New;
    p->v=v;
    p->nxt=adj[u];
    adj[u]=p;

    p=++New;
    p->v=u;
    p->nxt=adj[v];
    adj[v]=p;
}

int fa[MAXN+10],dep[MAXN+10];
int in[MAXN+10],out[MAXN+10];
int dfn=0;
void dfs(int x,int f){
    in[x]=++dfn,fa[x]=f,dep[x]=dep[f]+1;
    for(node *p=adj[x];p!=NULL;p=p->nxt)
        if(p->v!=f)dfs(p->v,x);
    out[x]=dfn;
}

int dp[MAXN+10][MAXLOG+3];
void init(){
    maxlog=log(n)/log(2);
    for(int i=1;i<=n;++i)dp[i][0]=fa[i];
    for(int len=1;len<=maxlog;++len)
        for(int i=1;i<=n;++i)dp[i][len]=dp[dp[i][len-1]][len-1];
}
int lca(int a,int b){
    if(dep[a]<dep[b])swap(a,b);
    for(int len=maxlog;len>=0;--len)
        if(dep[dp[a][len]]>=dep[b])a=dp[a][len];
    if(a==b)return a;
    for(int len=maxlog;len>=0;--len)
        if(dp[a][len]^dp[b][len])a=dp[a][len],b=dp[b][len];
    return fa[a];
}
int climb(int a,int d){
    int len=0;
    while(d){
        if(d&1)a=dp[a][len];
        d>>=1;
        ++len;
    }
    return a;
}

int u[MAXP+10],v[MAXP+10],w[MAXP+10];
int pos[MAXP+10];
bool cmpdish(const int &x,const int &y){
    return w[x]<w[y];
}

int main(){
     Read(n),Read(p),Read(q);
     int a,b,c;
     for(int i=1;i<n;++i){
        Read(a),Read(b);
        addedges(a,b);
     }

     dfs(1,0);
     init();

     for(int i=1;i<=p;++i){
        Read(u[i]),Read(v[i]),Read(w[i]);
        pos[i]=i;
     }
     sort(pos+1,pos+p+1,cmpdish);

     for(int i=1;i<=p;++i){
        rt[i]=rt[i-1];

        a=u[pos[i]],b=v[pos[i]],c=w[pos[i]];
        if(in[a]>in[b])swap(a,b);
        int ff=lca(a,b);
        if(ff==a){
            int tmp=climb(b,dep[b]-dep[ff]-1);
            if(in[tmp]>1){
                insert_line(1,in[tmp]-1,in[b],out[b],1,n,rt[i],rt[i]);
                //printf("Add: (%d,%d)(%d,%d)\n",1,in[tmp]-1,in[b],out[b]);
            }
            if(out[tmp]<n){
                insert_line(in[b],out[b],out[tmp]+1,n,1,n,rt[i],rt[i]);
                //printf("Add: (%d,%d)(%d,%d)\n",in[b],out[b],out[tmp]+1,n);
            }
        }
        else{
            insert_line(in[a],out[a],in[b],out[b],1,n,rt[i],rt[i]);
            //printf("Add: (%d,%d)(%d,%d)\n",in[a],out[a],in[b],out[b]);
        }
     }

     for(int i=1;i<=q;++i){
        Read(a),Read(b),Read(c);
        if(in[a]>in[b])swap(a,b);
        //printf("Query: (%d,%d)\n",in[a],in[b]);

        int l=1,r=p,mid;
        int ans=0;
        while(l<=r){
            mid=(l+r)>>1;
            if(query_line(in[a],in[b],1,n,rt[mid])>=c){
                ans=mid;
                r=mid-1;
            }
            else l=mid+1;
        }

        printf("%d\n",w[pos[ans]]);
     }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值