[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\n",b[ans[i]]); return 0; }