zoukankan      html  css  js  c++  java
  • [HNOI2015]接水果

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

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

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

    Sample Input
    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

    Sample Output
    442139372
    333042360
    442139372
    283254485
    283254485
    217394434
    217394434
    217394434
    217394434
    217394434

    HINT
    N,P,Q<=40000。


    考虑一下题型的转化

    我们可以把每个水果路径端点的dfs序提出来,将其变成一个平面内的点

    然后我们考虑一个盘子(u,v)能够接到哪些水果(l,r)(我们这里令dfn[u]<dfn[v],dfn[l]<dfn[r])

    如果lca为u,那么这个盘子能接到的水果的l,r需要满足

    (egin{cases}1&leqslant l'<x'\v'&leqslant r'<v'+size[v]end{cases})(egin{cases}x'+size[x]&<l'leqslant n\v'&leqslant r'<v'+size[v]end{cases})

    其中,l',r'等表示dfn[l],dfn[r]...,size[v]表示以v为根的子树大小,x为v到u路径上,u的儿子节点

    如果lca不为u,那么这个盘子能接到的水果的l,r需要满足

    [egin{cases}u'leqslant l'<u'+size[u]\v'leqslant r'<v'+size[v]end{cases} ]

    这样的话我们可以发现,每个盘子都可以表示成平面内的一个矩阵,如果有某个点在其覆盖范围内,就说明这个点,也就是这个点表示的水果能被这个矩阵表示的盘子接到

    于是问题转化为了给定平面内一些点和一些带权值的矩阵,对于每个点求出覆盖其的矩阵权值的第k小

    这样的话我们可以使用整体二分,当然也可以使用树套树,这里我采用后面一种写法

    我们将平面中x轴排序,使用扫描线,如果使用Lazy标记的话,空间肯定开不下,因此我们采用标记永久化。

    我们使用权值线段树套位置线段树,反过来的话求第(k)大较为麻烦

    /*program from Wolfycz*/
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define Fi first
    #define Se second
    #define inf 0x7f7f7f7f
    using namespace std;
    typedef long long ll;
    typedef unsigned int ui;
    typedef unsigned long long ull;
    inline char gc(){
    	static char buf[1000000],*p1=buf,*p2=buf;
    	return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
    }
    inline int frd(){
    	int x=0,f=1;char ch=gc();
    	for (;ch<'0'||ch>'9';ch=gc())	if (ch=='-')    f=-1;
    	for (;ch>='0'&&ch<='9';ch=gc())	x=(x<<1)+(x<<3)+ch-'0';
    	return x*f;
    }
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	for (;ch<'0'||ch>'9';ch=getchar())	if (ch=='-')	f=-1;
    	for (;ch>='0'&&ch<='9';ch=getchar())	x=(x<<1)+(x<<3)+ch-'0';
    	return x*f;
    }
    inline void print(int x){
    	if (x<0)    putchar('-'),x=-x;
    	if (x>9)	print(x/10);
    	putchar(x%10+'0');
    }
    const int N=4e4,M=2e7;
    int list[N+10],dfn[N+10],Ans[N+10];
    int n,P,Q;
    struct S1{
    	int pre[(N<<1)+10],now[N+10],child[(N<<1)+10],tot,Time;
    	int fa[N+10],deep[N+10],top[N+10],Rem[N+10],size[N+10];
    	void join(int x,int y){pre[++tot]=now[x],now[x]=tot,child[tot]=y;}
    	void insert(int x,int y){join(x,y),join(y,x);}
    	void dfs(int x){
    		deep[x]=deep[fa[x]]+1,size[x]=1;
    		for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
    			if (son==fa[x])	continue;
    			fa[son]=x,dfs(son);
    			size[x]+=size[son];
    			if (size[Rem[x]]<size[son])	Rem[x]=son;
    		}
    	}
    	void build(int x){
    		if (!x)	return;
    		dfn[x]=++Time;
    		top[x]=Rem[fa[x]]==x?top[fa[x]]:x;
    		build(Rem[x]);
    		for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
    			if (son==fa[x]||son==Rem[x])	continue;
    			build(son);
    		}
    	}
    	pair<int,int> Lca(int x,int y){
    		int Last=0;
    		while (top[x]!=top[y]){
    			if (deep[top[x]]<deep[top[y]])	swap(x,y);
    			Last=top[x],x=fa[top[x]];
    		}
    		if (x==y)	return make_pair(x,Last);
    		if (deep[x]>deep[y])	swap(x,y);
    		return make_pair(x,Rem[x]);
    	}
    }HLD;//Heavy Light Decomposition
    struct S2{
    	int root[(N<<2)+10];
    	struct S3{
    		int ls[M+10],rs[M+10],cnt[M+10],tot;
    		void Modify(int &p,int l,int r,int x,int y,int type){
    			if (!p)	p=++tot;
    			if (x<=l&&r<=y){
    				cnt[p]+=type;
    				return;
    			}
    			int mid=(l+r)>>1;
    			if (x<=mid)	Modify(ls[p],l,mid,x,y,type);
    			if (y>mid)	Modify(rs[p],mid+1,r,x,y,type);
    		}
    		int Query(int p,int l,int r,int x){
    			if (l==r)	return cnt[p];
    			int mid=(l+r)>>1;
    			if (x<=mid)	return Query(ls[p],l,mid,x)+cnt[p];
    			else	return Query(rs[p],mid+1,r,x)+cnt[p];
    		}
    	}PST;//Position Segment Tree
    	#define ls (p<<1)
    	#define rs (p<<1|1)
    	void Modify(int p,int l,int r,int v,int x,int y,int type){
    		PST.Modify(root[p],1,n,x,y,type);
    		if (l==r)	return;
    		int mid=(l+r)>>1;
    		if (v<=mid)	Modify(ls,l,mid,v,x,y,type);
    		else	Modify(rs,mid+1,r,v,x,y,type);
    	}
    	int Query(int p,int l,int r,int k,int x){
    		if (l==r)	return list[l];
    		int mid=(l+r)>>1,res=PST.Query(root[ls],1,n,x);
    		if (k<=res)	return Query(ls,l,mid,k,x);
    		else	return Query(rs,mid+1,r,k-res,x);
    	}
    	#undef ls
    	#undef rs
    }PST_VST;//Position Segment Tree in Value Segment Tree
    struct S4{
    	int x,l,r,k,type;
    	void insert(int _x,int _l,int _r,int _k,int _type){x=_x,l=_l,r=_r,k=_k,type=_type;}
    	bool operator <(const S4 &tis)const{return x<tis.x;}
    }plate[(N<<3)+10];
    struct S5{
    	int x,y,k,ID;
    	void insert(int _x,int _y,int _k,int _ID){x=_x,y=_y,k=_k,ID=_ID;}
    	bool operator <(const S5 &tis)const{return x<tis.x;}
    }fruit[N+10];
    int cnt;
    void insert(int L,int R,int l,int r,int k){
    	plate[++cnt].insert(L,l,r,k,1);
    	plate[++cnt].insert(R+1,l,r,k,-1);
    }
    void Ins(int L,int R,int l,int r,int k){
    	if (L>R)	return;
    	insert(L,R,l,r,k);
    	insert(l,r,L,R,k);
    }
    int main(){
    	n=read(),P=read(),Q=read();
    	for (int i=1;i<n;i++){
    		int x=read(),y=read();
    		HLD.insert(x,y);
    	}
    	HLD.dfs(1),HLD.build(1);
    	for (int i=1;i<=P;i++){
    		int x=read(),y=read(),k=read();
    		pair<int,int>tmp=HLD.Lca(x,y);
    		if (dfn[x]>dfn[y])	swap(x,y);
    		if (tmp.Fi==x){
    			Ins(1,dfn[tmp.Se]-1,dfn[y],dfn[y]+HLD.size[y]-1,k);
    			Ins(dfn[tmp.Se]+HLD.size[tmp.Se],n,dfn[y],dfn[y]+HLD.size[y]-1,k);
    		}else	Ins(dfn[x],dfn[x]+HLD.size[x]-1,dfn[y],dfn[y]+HLD.size[y]-1,k);
    		list[i]=k;
    	}
    	sort(list+1,list+1+P);
    	int T=unique(list+1,list+1+P)-list-1;
    	for (int i=1;i<=cnt;i++)	plate[i].k=lower_bound(list+1,list+1+T,plate[i].k)-list;
    	for (int i=1;i<=Q;i++){
    		int x=read(),y=read(),k=read();
    		if (dfn[x]>dfn[y])	swap(x,y);
    		fruit[i].insert(dfn[x],dfn[y],k,i);
    	}
    	sort(fruit+1,fruit+1+Q);
    	sort(plate+1,plate+1+cnt);
    	for (int i=1,j=1;i<=Q;i++){
    		while (j<=cnt&&plate[j].x<=fruit[i].x)	PST_VST.Modify(1,1,n,plate[j].k,plate[j].l,plate[j].r,plate[j].type),j++;
    		Ans[fruit[i].ID]=PST_VST.Query(1,1,n,fruit[i].k,fruit[i].y);
    	}
    	for (int i=1;i<=Q;i++)	printf("%d
    ",Ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    随机森林算法参数调优
    BAYES和朴素BAYES
    阿里云 金融接口 token PHP
    PHP mysql 按时间分组 表格table 跨度 rowspan
    MySql按周,按月,按日分组统计数据
    PHP 获取今日、昨日、本周、上周、本月的等等常用的起始时间戳和结束时间戳的时间处理类
    thinkphp5 tp5 会话控制 session 登录 退出 检查检验登录 判断是否应该跳转到上次url
    微信 模板消息
    php 腾讯 地图 api 计算 坐标 两点 距离 微信 网页 WebService API
    php添加http头禁止浏览器缓存
  • 原文地址:https://www.cnblogs.com/Wolfycz/p/10000261.html
Copyright © 2011-2022 走看看