zoukankan      html  css  js  c++  java
  • 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(nlog_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
    ",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;
    }
    
  • 相关阅读:
    在线jq库
    解决python3.6的UnicodeEncodeError: 'gbk' codec can't encode character 'xbb' in position 28613: illegal multibyte sequence
    PHP后台支付的开发:微信支付和支付宝支付
    PHP操控Excel视频教程
    微信h5静默、非静默授权获取用户openId的方法和步骤
    OAuth2.0微信网页授权登录
    微信第三方登录 -- (PC端+移动端)
    web字体规范
    移动端字体设置
    在 Web 内容中使用系统字体
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9535458.html
Copyright © 2011-2022 走看看