1. 程式人生 > >●洛谷P3242 [HNOI2015]接水果

●洛谷P3242 [HNOI2015]接水果

problem tin 樹套樹 end owb gis div 平衡樹 bool

題鏈:

https://www.luogu.org/problemnew/show/P3242

題解:

整體二分,掃描線+樹狀數組。
詳細的題解:http://blog.csdn.net/thy_asdf/article/details/50363672
得到各個盤子影響的矩形區域後,
那麽我們就是要對每個詢問代表的點查詢覆蓋了它的權值第k小的的那個矩形。
首先有一個簡化版的問題,
就是查詢改點被覆蓋了多少次。可以用掃描線+樹狀數組做。
然後對於現在詢問的第k小權值,
我們就可以二分答案,即二分一個權值mid,
然後判斷是否這些權值小於mid的矩形可以覆蓋該點至少k次,
使得話,縮小r,否則擴大l範圍。


由於有多個詢問,且分別做二分的話過程是一樣的,所以可以整體二分。

另外,還可以對當前掃描線上的東西建立樹套樹,
外面是權值線段樹,裏面套的是區間線段樹(方便區間修改,單點查詢),也可以求出第k小的權值。
如果想看具體實現的話,推薦服用這位博主的代碼:http://blog.csdn.net/make_it_for_good/article/details/52985365

再另外,好像還可以樹上莫隊+平衡樹做,但是我太弱了,忘了樹上莫隊,就沒寫了。
但是我感覺如果用莫隊的話,如果數據故意把水果和盤子的左端點放到同一個位置附近,
同時程序的分塊又恰好把這些位置分在了同一個塊,
可能會被卡成n^2的。

代碼:

#include<bits/stdc++.h>
#define MAXN 160008
#define rint register int
using namespace std;
int N,P,Q,snt;
int ANS[MAXN],fa[MAXN][18],deep[MAXN],be[MAXN],en[MAXN];
struct Edge{
	int ent;
	int to[MAXN],nxt[MAXN],head[MAXN];
	Edge(){ent=2;}
	void Adde(int u,int v){
		to[ent]=v; nxt[ent]=head[u]; head[u]=ent++;
	}
}E;
struct Plate{
	int x1,x2,y1,y2,val;
}S[MAXN];
struct info{
	int x,yl,yr,val,id;
}A[MAXN],T[MAXN];
bool cmp1(const Plate &_A,const Plate &_B){
	return _A.val<_B.val;
}
bool cmp2(const info &_A,const info &_B){
	return _A.x<_B.x||(_A.x==_B.x&&_A.id<_B.id);
}
struct BIT{
	int val[MAXN],n;
	void Reset(int _n){n=_n;}
	int Lowbit(int x){return x&(-x);}
	void Modify(int l,int r,int x){//區間修改
		if(l>r) return;
		for(rint i=l;i<=n;i+=Lowbit(i)) val[i]+=x;
		for(rint i=r+1;i<=n;i+=Lowbit(i)) val[i]-=x;
	}
	int Query(int p,int ret=0){//查詢覆蓋次數 
		for(rint i=p;i>=1;i-=Lowbit(i)) ret+=val[i];
		return ret;
	}
}DT;
void dfs(int u,int dad){
	static int cnt; ++cnt;
	be[u]=cnt; fa[u][0]=dad;
	for(int k=1;k<18;k++)
		fa[u][k]=fa[fa[u][k-1]][k-1];
	for(int e=E.head[u];e;e=E.nxt[e]){
		int v=E.to[e]; if(v==dad) continue;
		deep[v]=deep[u]+1; dfs(v,u);
	}
	en[u]=cnt;
}
int jump(int x,int h){
	for(int k=17;k>=0;k--) 
		if(h>=(1<<k)) x=fa[x][k],h-=(1<<k);
	return x;
}
info inssegment(const Plate &rtm,int k){
	static int x1,x2,y1,y2,v;
	x1=rtm.x1; x2=rtm.x2; y1=rtm.y1; y2=rtm.y2; v=rtm.val;
	assert(x1<=x2);
	if(k==1) return (info){x1,y1,y2,v,-1};
	else return (info){x2+1,y1,y2,v,-2};
}
void solve(int sl,int sr,int ql,int qr){
	static int sum[MAXN];
	if(ql>qr) return;
	if(sl==sr){
		for(int i=ql;i<=qr;i++) ANS[A[i].id]=S[sl].val;
		return;
	}
	int mid=(sl+sr)>>1,tnt=0,qlnt=ql-1,qrnt=0;
	for(int i=sl;i<=mid;i++) T[++tnt]=inssegment(S[i],1),T[++tnt]=inssegment(S[i],2);
	for(int i=ql;i<=qr;i++) T[++tnt]=A[i];
	sort(T+1,T+tnt+1,cmp2);
	for(int i=1;i<=tnt;i++){
		if(T[i].id<0) DT.Modify(T[i].yl,T[i].yr,T[i].id==-1?1:-1);
		else sum[T[i].id]=DT.Query(T[i].yl);
	}
	for(int i=1;i<=tnt;i++){
		if(T[i].id<0) DT.Modify(T[i].yl,T[i].yr,T[i].id==-1?-1:1);
		else{
			if(sum[T[i].id]>=T[i].val) A[++qlnt]=T[i];
			else T[i].val-=sum[T[i].id],T[++qrnt]=T[i];
		}
	}
	for(int i=1;i<=qrnt;i++) A[i+qlnt]=T[i];		
	solve(sl,mid,ql,qlnt);
	solve(mid+1,sr,qlnt+1,qr);
}
int main(){
	ios::sync_with_stdio(0);
	cin>>N>>P>>Q;
	for(int i=1,a,b;i<N;i++)
		cin>>a>>b,E.Adde(a,b),E.Adde(b,a);
	dfs(1,0); DT.Reset(N);
	for(int i=1,x,y,v,t;i<=P;i++){
		cin>>x>>y>>v;
		if(be[x]>be[y]) swap(x,y); 
		if(be[x]<=be[y]&&en[x]>=en[y]){
			t=jump(y,deep[y]-deep[x]-1);
			S[++snt]=(Plate){1,be[t]-1,be[y],en[y],v};
			if(en[t]+1<=N) S[++snt]=(Plate){be[y],en[y],en[t]+1,N,v};
		}
		else S[++snt]=(Plate){be[x],en[x],be[y],en[y],v};
	}
	sort(S+1,S+snt+1,cmp1);
	for(int i=1,x,y,k;i<=Q;i++){
		cin>>x>>y>>k;
		if(be[x]>be[y]) swap(x,y);
		A[i]=(info){be[x],be[y],0,k,i};
		//在構建的二維平面裏,x點對應的下標不是x,而是be[x]
		//wa了一次,就是因為上面的那行代碼寫成了:
		//A[i]=(info){x,y,0,k,i};
	}
	solve(1,snt,1,Q);
	for(int i=1;i<=Q;i++) cout<<ANS[i]<<endl;
	return 0;
}

  

●洛谷P3242 [HNOI2015]接水果