[HNOI2015]接水果

题目描述

风见幽香非常喜欢玩一个叫做 osu!的游戏,其中她最喜欢玩的模式就是接水果。由于她已经DT FC 了The big black, 她觉得这个游戏太简单了,于是发明了一个更加难的版本。

首先有一个地图,是一棵由 n 个顶点、n-1 条边组成的树(例如图 1给出的树包含 8 个顶点、7 条边)。

这颗树上有 P 个盘子,每个盘子实际上是一条路径(例如图 1 中顶点 6 到顶点 8 的路径),并且每个盘子还有一个权值。第 i 个盘子就是顶点a_i到顶点b_i的路径(由于是树,所以从a_i到b_i的路径是唯一的),权值为c_i。

接下来依次会有Q个水果掉下来,每个水果本质上也是一条路径,第i 个水果是从顶点 u_i 到顶点v_i 的路径。

幽香每次需要选择一个盘子去接当前的水果:一个盘子能接住一个水果,当且仅当盘子的路径是水果的路径的子路径(例如图1中从 3到7 的路径是从1到8的路径的子路径)。这里规定:从a 到b的路径与从b到 a的路径是同一条路径。

当然为了提高难度,对于第 i 个水果,你需要选择能接住它的所有盘子中,权值第 k_i 小的那个盘子,每个盘子可重复使用(没有使用次数的上限:一个盘子接完一个水果后,后面还可继续接其他水果,只要它是水果路径的子路径)。幽香认为这个游戏很难,你能轻松解决给她看吗?

题解

这道题的关键就是如何判断路径完全覆盖的情况。

考虑一条路径u->v,dfn[u]<dfn[v],另一条路径x->y,dfn[x]<dfn[y]xy要覆盖uv需要满足什么条件。

先考虑lca(u,v)!=u的情况,那么x必须在u的子树里,y必须在v的子树里。

所以x€dfn[u],dfn[u]+size[u]-1,y€dfn[v],dfn[v]+size[v]-1.

然后是lca(u,v)==u

这时下面的点必须要在v的子树内部,上面的点不能在u的子树内部(但可以在u上)。

前面的东西很好求,但是后面的东西感觉非常复杂,因为它既包括u的子树外的,又包括u的某些其他儿子的子树。

但我们仔细一想,好像这里的不合法区域是一段连洗的区间。

我们找出u下面的点v,不合法的区域就是v的子树。

然后我们把两个点看成一个点的横纵坐标,那么两个点的限制相当于是一个矩形,那么一个询问相当于是从所有覆盖它的矩形中找出权值的第k小。

整体二分+扫描线+树状数组就好了,这块就比较套路了。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 40009
using namespace std;
typedef long long ll;
const int mod=998244353;
int tot,deep[N],head[N],p[N][19],dfn[N],top,size[N],tr[N],tott,tottp,n,ans[N],b[N],pp,q;
inline ll rd(){
    ll x=0;char c=getchar();bool f=0;
    while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return f?-x:x;
}
struct edge{int n,to;}e[N<<1];
inline void add(int u,int v){e[++tot].n=head[u];e[tot].to=v;head[u]=tot;}
void dfs(int u,int fa){
    dfn[u]=++top;
    size[u]=1;
    for(int i=1;(1<<i)<=deep[u];++i)
      p[u][i]=p[p[u][i-1]][i-1];
    for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa){
        int v=e[i].to;deep[v]=deep[u]+1;p[v][0]=u;
        dfs(v,u);
        size[u]+=size[v];
    }
}
inline int getlca(int a,int b){
    if(deep[a]<deep[b])swap(a,b);
    for(int i=18;i>=0;--i)if(deep[a]-(1<<i)>=deep[b])a=p[a][i];
    if(a==b)return a;
    for(int i=18;i>=0;--i)if(p[a][i]!=p[b][i])a=p[a][i],b=p[b][i];
    return p[a][0];
}
inline void upd(int x,int y){while(x<=(n+5))tr[x]+=y,x+=x&-x;}
inline int query(int x){int ans=0;while(x)ans+=tr[x],x-=x&-x;return ans;}
struct matrix{
    int x,yf,ys,tag;ll num;    
    int id;
}m[N*5],m1[N*5],m2[N*5];
inline bool cmptim(matrix a,matrix b){
    if(a.x!=b.x)return a.x<b.x;
    else return a.tag>b.tag;
}
void upd2(int x,int y,int num){
    upd(x,num);upd(y+1,-num);
}
void solve(int l,int r,int L,int R){
    if(L==R){
        for(int i=l;i<=r;++i)if(m[i].id)ans[m[i].id]=L;
        return;
    }
    int mid=(L+R)>>1;
    int p1=0,p2=0;
    for(int i=l;i<=r;++i){
        if(m[i].id){
            int xx=query(m[i].yf);
            if(xx>=m[i].num)m1[++p1]=m[i];
            else m[i].num-=xx,m2[++p2]=m[i];
        }
        else{
            if(m[i].num<=b[mid])upd2(m[i].yf,m[i].ys,m[i].tag),m1[++p1]=m[i];
            else m2[++p2]=m[i];
        }
    }
    for(int i=1;i<=p1;++i){
      if(!m1[i].id)upd2(m1[i].yf,m1[i].ys,-m1[i].tag);
      m[l+i-1]=m1[i];
    }
    for(int i=1;i<=p2;++i)m[l+p1+i-1]=m2[i];
    solve(l,l+p1-1,L,mid);solve(l+p1,r,mid+1,R);
}
inline int jump(int x,int dep){
    for(int i=18;i>=0;--i)if((1<<i)&dep)x=p[x][i];
    return x;
}
int main(){
    n=rd();pp=rd();q=rd();int u,v,w;
    for(int i=1;i<n;++i){
        u=rd();v=rd();
        add(u,v);add(v,u);
    }
    dfs(1,0);
    for(int i=1;i<=pp;++i){
        u=rd();v=rd();w=rd();b[++tottp]=w;
        if(dfn[u]>dfn[v])swap(u,v);
        int lca=getlca(u,v);
        if(lca==u){
            int x=jump(v,deep[v]-deep[u]-1);
            m[++tott]=matrix{1,dfn[v],dfn[v]+size[v]-1,1,w,0};
            m[++tott]=matrix{dfn[x],dfn[v],dfn[v]+size[v]-1,-1,w,0};
            m[++tott]=matrix{dfn[v],dfn[x]+size[x],n,1,w,0};
            m[++tott]=matrix{dfn[v]+size[v],dfn[x]+size[x],n,-1,w,0};
        } 
        else{
            m[++tott]=matrix{dfn[u],dfn[v],dfn[v]+size[v]-1,1,w,0};
            m[++tott]=matrix{dfn[u]+size[u],dfn[v],dfn[v]+size[v]-1,-1,w,0};
        }
    }
    sort(b+1,b+tottp+1);tottp=unique(b+1,b+tottp+1)-b-1;
    for(int i=1;i<=q;++i){
        m[++tott].x=rd();m[tott].yf=rd();m[tott].num=rd();m[tott].id=i;m[tott].tag=-2;
        m[tott].x=dfn[m[tott].x];m[tott].yf=dfn[m[tott].yf]; 
        if(m[tott].x>m[tott].yf)swap(m[tott].x,m[tott].yf);
    }
    sort(m+1,m+tott+1,cmptim);
    solve(1,tott,1,tottp);
    for(int i=1;i<=q;++i)printf("%d
",b[ans[i]]);
    return 0;
}
原文地址:https://www.cnblogs.com/ZH-comld/p/10287006.html