BZOJ4009: [HNOI2015]接水果

BZOJ4009: [HNOI2015]接水果

由于$BZOJ$上经常有人拿这道题卡评测姬,所以变成了权限题。。。

本蒟蒻表示没钱氪金。。。

这里附上洛谷的体面:

P3242 [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 小的那个盘子,每个盘子可重复使用(没有使用次数的上限:一个盘子接完一个水果后,后面还可继续接其他水果,只要它是水果路径的子路径)。幽香认为这个游戏很难,你能轻松解决给她看吗? 

输入输出格式

输入格式:

第一行三个数 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 小一定存在。

输出格式:

对于每个果子,输出一行表示选择的盘子的权值。

输入输出样例

输入样例#1:  复制
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
输出样例#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;
}

 

转载于:https://www.cnblogs.com/Yangrui-Blog/p/9535458.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值