BZOJ4009: [HNOI2015]接水果
由于$BZOJ$上经常有人拿这道题卡评测姬,所以变成了权限题。。。
本蒟蒻表示没钱氪金。。。
这里附上洛谷的体面:
题目描述
风见幽香非常喜欢玩一个叫做 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 小的那个盘子,每个盘子可重复使用(没有使用次数的上限:一个盘子接完一个水果后,后面还可继续接其他水果,只要它是水果路径的子路径)。幽香认为这个游戏很难,你能轻松解决给她看吗?
输入输出格式
输入格式:
第一行三个数 n和P 和Q,表示树的大小和盘子的个数和水果的个数。 接下来n-1 行,每行两个数 a、b,表示树上的a和b 之间有一条边。树中顶点按1到 n标号。 接下来 P 行,每行三个数 a、b、c,表示路径为 a 到 b、权值为 c 的盘子,其中0<=c<=10^9,a不等于b。 接下来Q行,每行三个数 u、v、k,表示路径为 u到 v的水果,其中 u不等于v,你需要选择第 k小的盘子,第k 小一定存在。
输出格式:
对于每个果子,输出一行表示选择的盘子的权值。
输入输出样例
10 10 10 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 3 2 217394434 10 7 13022269 6 7 283254485 6 8 333042360 4 6 442139372 8 3 225045590 10 4 922205209 10 8 808296330 9 2 486331361 4 9 551176338 1 8 5 3 8 3 3 8 4 1 8 3 4 8 1 2 3 1 2 3 1 2 3 1 2 4 1 1 4 1
442139372 333042360 442139372 283254485 283254485 217394434 217394434 217394434 217394434 217394434
说明
N,P,Q<=40000。
题解Here!
题目大意:给定一棵树和$m$条路径,每条路径有一个权值,$q$次询问,每次询问某条路经包含的所有路径中权值的第$k$小。
本蒟蒻表示并不会整体二分/CDQ分治。。。
所以怒写树链剖分+权值线段树套动态开点区间线段树然后$1A$。。。
然后我$4h$就搭上面了。。。
考虑满足什么条件的水果$(x_1,y_1)$会落在指定盘子$(x_2,y_2)$上:
如果$x_2,y_2$为祖先孩子关系,设$x_2$是$y_2$的祖先,$t$是$x_2$到$y_2$路径上第二个点,那么$x_1,y_1$一定满足$x_1$在$y_2$子树中,$y_1$在$x_2$子树外。
盗一张图:
在子树中可以表示为$dfs$序中一段,在子树外可以表示为两段。
但是我为了偷点懒,直接写了个树链剖分。。。
否则$x_1$一定在$x_2$子树中,$y_1$一定在$y_2$子树中。
再盗一张图:
在我的程序里面$last[x]==id[x]+size[x]-1$
这样问题就转化成平面上一些矩形,每个矩形有权值,一些询问,求包含一个点的权值第$k$小的矩形。
离线下来,把一维的矩形坐标拆成在$x_1$处$+1$,$x_2+1$处$-1$。
排一下序。然后就变成了一个二维问题。
需要普通线段树维护位置,权值线段树维护第$K$大。
由于外层树结构不支持区间修改,因此需要权值线段树套区间线段树。
所以总复杂度为$O(n\log_2^2n)$。
附代码:(一大堆为了调试而自造的分隔符。。。)
#include<iostream> #include<algorithm> #include<cstdio> #define MAXN 40010 using namespace std; //---------------------------------------------------------------------------------------------------- int n,m,q,num=0,c=1,d=1,num_plate=0; int head[MAXN],deep[MAXN],son[MAXN],size[MAXN],fa[MAXN],id[MAXN],pos[MAXN],top[MAXN]; int root[MAXN<<2],lsh[MAXN],ans[MAXN]; //---------------------------------------------------------------------------------------------------- struct Tree{ int next,to; }edge[MAXN<<1]; //---------------------------------------------------------------------------------------------------- struct Plate{ int x,l,r,val,c; friend bool operator <(const Plate &p,const Plate &q){ if(p.x==q.x)return p.c>q.c; return p.x<q.x; } }plate[MAXN<<3]; //---------------------------------------------------------------------------------------------------- struct Question{ int l,r,k,id; friend bool operator <(const Question &p,const Question &q){ return p.l<q.l; } }que[MAXN]; //---------------------------------------------------------------------------------------------------- inline int read(){ int date=0,w=1;char c=0; while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date*w; } //---------------------------------------------------------------------------------------------------- namespace ST{ #define DATA(x) a[x].data #define LSIDE(x) a[x].lson #define RSIDE(x) a[x].rson int size=0; struct Segment_Tree{ int data,lson,rson; }a[MAXN*500]; void insert(int l,int r,int c,int lside,int rside,int &rt){ if(!rt)rt=++size; if(l<=lside&&rside<=r){ DATA(rt)+=c; return; } int mid=lside+rside>>1; if(l<=mid)insert(l,r,c,lside,mid,LSIDE(rt)); if(mid<r)insert(l,r,c,mid+1,rside,RSIDE(rt)); } int query(int l,int r,int lside,int rside,int rt){ if(!rt)return 0; if(l<=lside&&rside<=r)return DATA(rt); int mid=lside+rside>>1,ans=DATA(rt); if(l<=mid)ans+=query(l,r,lside,mid,LSIDE(rt)); if(mid<r)ans+=query(l,r,mid+1,rside,RSIDE(rt)); return ans; } } //---------------------------------------------------------------------------------------------------- namespace CT{ #define LSON rt<<1 #define RSON rt<<1|1 void insert(int k,int v,int l,int r,int rt){ ST::insert(plate[k].l,plate[k].r,plate[k].c,1,n,root[rt]); if(l==r)return; int mid=l+r>>1; if(v<=mid)insert(k,v,l,mid,LSON); else insert(k,v,mid+1,r,RSON); } int query(int k,int v,int l,int r,int rt){ if(l==r)return lsh[l]; int mid=l+r>>1,t=ST::query(v,v,1,n,root[LSON]); if(k<=t)return query(k,v,l,mid,LSON); else return query(k-t,v,mid+1,r,RSON); } } //---------------------------------------------------------------------------------------------------- inline void add_edge(int x,int y){ edge[c].to=y;edge[c].next=head[x];head[x]=c++; edge[c].to=x;edge[c].next=head[y];head[y]=c++; } inline void new_plate(int l1,int r1,int l2,int r2,int val){ num_plate++; plate[num_plate].x=l1;plate[num_plate].l=l2;plate[num_plate].r=r2;plate[num_plate].val=val; plate[num_plate].c=1; //-------------------------------------------------------------------------------- num_plate++; plate[num_plate].x=r1+1;plate[num_plate].l=l2;plate[num_plate].r=r2;plate[num_plate].val=val; plate[num_plate].c=-1; } inline void add_plate(int l1,int r1,int l2,int r2,int val){ new_plate(l1,r1,l2,r2,val); new_plate(l2,r2,l1,r1,val); } inline void add_que(int l,int r,int k,int i){ que[i].l=l;que[i].r=r;que[i].k=k; que[i].id=i; } //---------------------------------------------------------------------------------------------------- void dfs1(int rt){ son[rt]=0;size[rt]=1; for(int i=head[rt];i;i=edge[i].next){ int will=edge[i].to; if(!deep[will]){ deep[will]=deep[rt]+1; fa[will]=rt; dfs1(will); size[rt]+=size[will]; if(size[son[rt]]<size[will])son[rt]=will; } } } void dfs2(int rt,int f){ id[rt]=d++;pos[id[rt]]=rt;top[rt]=f; if(son[rt])dfs2(son[rt],f); for(int i=head[rt];i;i=edge[i].next){ int will=edge[i].to; if(will!=fa[rt]&&will!=son[rt])dfs2(will,will); } } inline int subtree(int x,int y){ while(deep[y]>deep[x]){ if(fa[top[y]]==x)return top[y]; y=fa[top[y]]; } return son[x]; } //---------------------------------------------------------------------------------------------------- void work(){ for(int i=1,now=1;i<=q;i++){ while(now<=num_plate&&plate[now].x<=que[i].l){ CT::insert(now,plate[now].val,1,num,1); now++; } ans[que[i].id]=CT::query(que[i].k,que[i].r,1,num,1); } for(int i=1;i<=q;i++)printf("%d\n",ans[i]); } //---------------------------------------------------------------------------------------------------- void init(){ int u,v,w; n=read();m=read();q=read(); for(int i=1;i<n;i++){ u=read();v=read(); add_edge(u,v); } deep[1]=1; dfs1(1); dfs2(1,1); //-------------------------------------------------------------------------------- for(int i=1;i<=m;i++){ u=read();v=read();w=read(); lsh[++num]=w; if(deep[u]>deep[v])swap(u,v); if(id[u]<=id[v]&&id[v]+size[v]<=id[u]+size[u]){ int x=subtree(u,v); if(id[x]>1)add_plate(id[v],id[v]+size[v]-1,1,id[x]-1,w); if(id[x]+size[x]<=n)add_plate(id[v],id[v]+size[v]-1,id[x]+size[x],n,w); } else add_plate(id[u],id[u]+size[u]-1,id[v],id[v]+size[v]-1,w); } sort(lsh+1,lsh+num+1); num=unique(lsh+1,lsh+num+1)-lsh-1; for(int i=1;i<=num_plate;i++)plate[i].val=lower_bound(lsh+1,lsh+num+1,plate[i].val)-lsh; sort(plate+1,plate+num_plate+1); //-------------------------------------------------------------------------------- for(int i=1;i<=q;i++){ u=read();v=read();w=read(); if(deep[u]>deep[v])swap(u,v); add_que(id[u],id[v],w,i); } sort(que+1,que+q+1); } //---------------------------------------------------------------------------------------------------- int main(){ init(); work(); return 0; }