[loj#2113] [HNOI2015] 接水果

题意简述

给定 \(n\) 个节点的树。
给定 \(P\) 个盘子,每个盘子是树上一条从 \(u\)\(v\) 的路径,有权值 \(c\)
给定 \(Q\) 个水果,每个水果是树上一条从 \(u\)\(v\) 的路径。
一个盘子能接住一个水果,当且仅当盘子的路径是水果的路径的子路径。

对每个水果,求可接住它的盘子中第 \(k_i\) 小的权值。

\(n,P,Q \leq 40000\)


想法

重点在于如何判断一条路径是否为另一条路径的子路径。

假设我们要找包含路径 \(u\)\(v\) 的路径(即路径 \(u\)\(v\) 是哪些路径的子路径)。
假设 \(u\)\(dfs\) 序更小,设 \(u\)\(v\)\(lca\)\(L\)
有两种情况:
1.\(L \neq u\)\(L \neq v\),即这条路径有拐弯
易知包含它的路径一端在 \(u\) 子树中,一端在 \(v\) 子树中
2.\(L=u\)
\(z\) 为这条路径上 \(u\) 下面的第一个点。则包含这条路径的路径一端在 \(v\) 子树中,另一端在 非\(z\)子树 的那些点中(画图可发现)

把每条路径投射到二维平面上的点 \((u,v)\)\(u,v\) 为路径的两端点且\(u\)\(dfs\) 序更小
发现上面两种情况对应的路径是二维平面上的一些矩形。

再回来考虑第 \(k\) 大,显然可以二分答案,然后扫描线+树状数组判断。
有多组询问,用整体二分。


总结

思想

1.树上路径投射成二维平面上的点 \((u,v)\)
2.扫描线不一定配线段树,树状数组常数更小。

码力

此处应划重点!!
1.数组大小!!!!!!!!!!特别注意不要以为有的注意了就说明所有都注意了,所以都还要好好检查!!!!!!
2.\(a!=b.a?a<b.a:c>b.c\) 这个判断一定要写清楚……别写反了……
3.整体二分中全局变量与局部变量的问题!!比如 \(cnt1,cnt2\) 应设为内部的变量,不然递归一般后大小变了
4.这个题差分比较多,想清楚。。
5.树状数组!我咋能忘了咋用呢。。。此题中是区间修改、单点查询,所以查询时不用减前面的了……写之前要搞清楚
6.别忘了离散化哦


代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<map>

using namespace std;

int read(){
	int x=0;
	char ch=getchar();
	while(!isdigit(ch)) ch=getchar();
	while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
	return x; 
}

const int N = 40005;

int n,pla,fru;

struct node{
	int v;
	node *nxt;
}pool[N*2],*h[N];
int cnt;
void addedge(int u,int v){
	node *p=&pool[++cnt],*q=&pool[++cnt];
	p->v=v;p->nxt=h[u];h[u]=p;
	q->v=u;q->nxt=h[v];h[v]=q;
}

int dfn[N],dep[N],fa[N][18],out[N],tot;
void dfs(int u){
	int v;
	dfn[u]=++tot;
	for(node *p=h[u];p;p=p->nxt)
		if(!dfn[v=p->v]) {
			fa[v][0]=u; dep[v]=dep[u]+1;
			for(int j=1;j<16;j++) fa[v][j]=fa[fa[v][j-1]][j-1];
			dfs(v);
		}
	out[u]=tot;
}
int fst(int u,int v){
	for(int i=15;i>=0;i--) if(dep[fa[v][i]]>dep[u]) v=fa[v][i];
	return v;
}

int mmp[N];
map<int,int> mp;
struct data{
	int a,l,r,c,k,id; //c==-1?fruit:plate
	bool operator < (const data &b) const{
		return a!=b.a?a<b.a:c>b.c;
	}
}d[N*6],t1[N*6],t2[N*6]; /*all N*6*/
int num;

int C[N];
void add(int x,int y) { while(x<=n) C[x]+=y,x+=x&(-x); }
int sum(int x) { int ret=0; while(x) ret+=C[x],x-=x&(-x); return ret; }

int ans[N];
void work(int l,int r,int L,int R){
	if(L>R || l>r) return;
	if(l==r){
		for(int i=L;i<=R;i++) if(d[i].c==-1) ans[d[i].id]=l;
		return;
	}
	int flag=0;
	for(int i=L;i<=R;i++) if(d[i].c==-1) flag=1;
	if(!flag) return;
	
	int mid=(l+r)>>1,s;
	int cnt1=0,cnt2=0; //inner
	for(int i=L;i<=R;i++){
		if(d[i].c==-1){
			s=sum(d[i].l); /*dandian query*/
			if(s<d[i].k) d[i].k-=s,t2[++cnt2]=d[i];
			else t1[++cnt1]=d[i];
		}
		else{
			if(d[i].c>mid) t2[++cnt2]=d[i];
			else {
				t1[++cnt1]=d[i];
				add(d[i].l,d[i].k); add(d[i].r+1,-d[i].k); /*r+1*/
			}
		}
	}
	for(int i=1;i<=cnt1;i++)
		if(t1[i].c!=-1) {
			add(t1[i].l,-t1[i].k);
			add(t1[i].r+1,t1[i].k); /*r+1*/
		}
	for(int i=1;i<=cnt1;i++) d[L+i-1]=t1[i];
	for(int i=1;i<=cnt2;i++) d[L+cnt1+i-1]=t2[i]; 
	work(l,mid,L,L+cnt1-1); work(mid+1,r,L+cnt1,R);
}

int main()
{
	n=read(); pla=read(); fru=read();
	for(int i=1;i<n;i++) addedge(read(),read());
	dep[1]=1; dfs(1);
	int mx=0,u,v,z,c;
	for(int i=1;i<=pla;i++){
		u=read(); v=read(); c=read(); mmp[i]=c;
		if(dfn[u]>dfn[v]) swap(u,v);
		if(dfn[v]<=out[u]) {//u->v:(1,dfn[v])-(dfn[z]-1,out[v]);(dfn[v],out[z]+1)-(out[v],n)
			z=fst(u,v); 
			if(dfn[z]-1>0){
				++num; d[num].a=1; d[num].l=dfn[v]; d[num].r=out[v]; d[num].c=c; d[num].k=1;
				++num; d[num].a=dfn[z]; d[num].l=dfn[v]; d[num].r=out[v]; d[num].c=c; d[num].k=-1;
			}
			if(out[z]+1<=n){
				++num; d[num].a=dfn[v]; d[num].l=out[z]+1; d[num].r=n; d[num].c=c; d[num].k=1;
				++num; d[num].a=out[v]+1; d[num].l=out[z]+1; d[num].r=n; d[num].c=c; d[num].k=-1;
			}
		}
		else{ //(dfn[u],dfn[v])-(out[u],out[v])
			++num; d[num].a=dfn[u]; d[num].l=dfn[v]; d[num].r=out[v]; d[num].c=c; d[num].k=1;
			++num; d[num].a=out[u]+1; d[num].l=dfn[v]; d[num].r=out[v]; d[num].c=c; d[num].k=-1;
		}
	}
	for(int i=1+num;i<=fru+num;i++){
		u=read(); v=read(); if(dfn[u]>dfn[v]) swap(u,v);
		d[i].a=dfn[u]; d[i].l=d[i].r=dfn[v]; d[i].c=-1; d[i].k=read(); d[i].id=i-num; /*dfn*/
	}
	num+=fru;
	
	sort(mmp+1,mmp+1+pla);
	mx=unique(mmp+1,mmp+1+pla)-mmp-1;
	for(int i=1;i<=mx;i++) mp[mmp[i]]=i;
	for(int i=1;i<=num;i++) 
		if(d[i].c!=-1) d[i].c=mp[d[i].c];
	
	sort(d+1,d+1+num); 
	work(1,mx,1,num);
	for(int i=1;i<=fru;i++) printf("%d\n",mmp[ans[i]]);
	
	return 0;
}
posted @ 2020-05-30 21:18  秋千旁的蜂蝶~  阅读(173)  评论(0编辑  收藏  举报