2021.5.8总结

再次垫底
开场发现A好像看过原题,是个单调栈,但是发现不太会做。
然后看B发现好像是模板题。
C也不太会做。
回头想A,可以过中线分治后合法的点集就是单调栈。
如果按照y降序排,则单调栈只有插入操作,容易维护。
左边的限制想了一想可以线段树+二分。
写完后就过了样例,然而手出的样例过不了。
手出的样例过了后,写了个暴力对拍拍出了错。
调了很久才拍上。。。。。
然后看B,B也是很久没调出来,暴力都没时间写。。。。。
交上去,A题被卡成暴力分,果然人傻常数大。
然而我在LibreOJ上过了,就算100分吧
最后只有100分。。。。。
总结:问题出在B的点分治树好久没写过了,忘记了线段树要开2n个,所以没写出来。
要复习一下点分治树的写法。

题解:
A:用经典的过中线分治。
先离散化。
一个左下端点合法的右上端点构成了单调栈中的单调点集:这个点集从左上到右下,(x)坐标单调递增,(y)坐标单调递减。
按照(y)坐标倒序排序所有点,顺序枚举所有左边点,把右边(y)坐标大于当前点的右边点插入单调栈
然而我们要考虑中线左边的限制。
事实上,我们发现这个限制可以用线段树维护。
建立线段树,维护区间最小值,初始所有元素为(inf)
扫到第(i)个点时,先查询把([x,md])的最小值(v)
发现当前点向上到(v-1)都是合法的。
由于单调栈中(x)递增,所以在单调栈上合法区间是连续的,可以二分。
然后把(x)位置的值修改成中点。
时间复杂度(O(nlog_2^2n))
不知道为什么跑的这么慢,但是在LibreOJ上过了

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define N 200010
#pragma GCC optimize(3) 
int n,x[N],y[N],st[N],tp,ans;
struct no{
	int x,y;
}a[N];
struct st{
	int a[N*5];
	void mod(int o,int l,int r,int x,int y){
		if(l==r){
			a[o]=y;
			return;
		}
		int md=(l+r)/2;
		if(x<=md)
			mod(o*2,l,md,x,y);
		else
			mod(o*2+1,md+1,r,x,y);
		a[o]=min(a[o*2],a[o*2+1]);
	}
	int qu(int o,int l,int r,int x,int y){
		if(r<x||y<l)
			return 1e9;
		if(x<=l&&r<=y)
			return a[o];
		int md=(l+r)/2;
		return min(qu(o*2,l,md,x,y),qu(o*2+1,md+1,r,x,y));
	}
}ss;
int operator <(no x,no y){
	return x.y>y.y;
}
vector<no>v;
void fz(int l,int r,vector<no>v){
	if(l>=r)
		return;
	int md=(l+r)/2;
	vector<no>v1,v2;
	for(int i=0;i<v.size();i++){
		no x=v[i];
		if(x.x<=md)
			v1.push_back(x);
		else
			v2.push_back(x);
	}
	tp=0;
	int j=0;
	for(int i=0;i<v1.size();i++){
		no x=v1[i];
		while(j!=v2.size()&&v2[j].y>x.y){
			while(tp&&v2[st[tp]].x>v2[j].x)
				tp--;
			st[++tp]=j;
			j++;
		}
		int v=ss.qu(1,1,n,x.x,md),l=1,r=tp,po=tp+1;
		while(l<=r){
			int md=(l+r)/2;
			if(v2[st[md]].y<=v){
				po=md;
				r=md-1;
			}
			else
				l=md+1;
		}
		ans+=tp-po+1;
		ss.mod(1,1,n,x.x,x.y);
	}
	for(int i=0;i<v1.size();i++){
		no x=v1[i];
		ss.mod(1,1,n,x.x,1e9);
	}
	fz(l,md,v1);
	fz(md+1,r,v2);
}
signed main(){
	freopen("scarecrows.in","r",stdin);
	freopen("scarecrows.out","w",stdout);
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
		scanf("%lld%lld",&a[i].x,&a[i].y);
	for(int i=1;i<=n;i++)
		ss.mod(1,1,n,i,1e9);
	for(int i=1;i<=n;i++){
		x[i]=a[i].x;
		y[i]=a[i].y;
	}
	sort(x+1,x+n+1);
	sort(y+1,y+n+1);
	for(int i=1;i<=n;i++){
		a[i].x=lower_bound(x+1,x+n+1,a[i].x)-x;
		a[i].y=lower_bound(y+1,y+n+1,a[i].y)-y;
	}
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++)
		v.push_back(a[i]);
	fz(1,n,v);
	printf("%lld",ans);
}

B:模板题
显然二分,问题转化成判定和一个点距离(leq d)的数是否(>val)
这显然可以用点分治树解决,在每个节点上维护线段树即可。

#include<bits/stdc++.h>
using namespace std;
#define N 80010
int n,h[N],w[N],v[N],nxt[N],ec,tp[N],sz[N],p[N],f[N],d[N],di[N],rt[N],lc[N*20],rc[N*20],ss[N*20],rr,ms[N],vi[N],ps,ff[N],ct;
void add(int x,int y,int z){
	v[++ec]=y;
	w[ec]=z;
	nxt[ec]=h[x];
	h[x]=ec;
}
void d1(int x,int fa){
	f[x]=fa;
	sz[x]=1;
	for(int i=h[x];i;i=nxt[i])
		if(v[i]!=fa){
			di[v[i]]=di[x]+w[i];
			d[v[i]]=d[x]+1;
			d1(v[i],x);
			sz[x]+=sz[v[i]];
			if(sz[v[i]]>sz[p[x]])
				p[x]=v[i];
		}
}
void d2(int x,int fa,int t){
	tp[x]=t;
	if(p[x])
		d2(p[x],x,t);
	for(int i=h[x];i;i=nxt[i])
		if(v[i]!=fa&&v[i]!=p[x])
			d2(v[i],x,v[i]);
}
int ll(int x,int y){
	while(tp[x]!=tp[y]){
		if(d[tp[x]]>d[tp[y]])
			x=f[tp[x]];
		else
			y=f[tp[y]];
	}
	if(d[x]<d[y])
		return x;
	return y;
}
int dd(int x,int y){
	return di[x]+di[y]-2*di[ll(x,y)];
}
void mod(int &o,int l,int r,int x,int y){
	if(!o)
		o=++ct;
	ss[o]+=y;
	if(l==r){
		return;
	}
	int md=(l+r)/2;
	if(x<=md)
		mod(lc[o],l,md,x,y);
	else
		mod(rc[o],md+1,r,x,y);
}
int qq(int o,int l,int r,int x,int y){
	if(r<x||y<l)
		return 0;
	if(x<=l&&r<=y)
		return ss[o];
	int md=(l+r)/2;
	return qq(lc[o],l,md,x,y)+qq(rc[o],md+1,r,x,y);
}
void d4(int x,int fa){
	sz[x]=1;
	ms[x]=0;
	for(int i=h[x];i;i=nxt[i])
		if(!vi[v[i]]&&v[i]!=fa){
			d4(v[i],x);
			sz[x]+=sz[v[i]];
		}
}
void gr(int x,int fa){
	for(int i=h[x];i;i=nxt[i])
		if(!vi[v[i]]&&v[i]!=fa){
			gr(v[i],x);
			ms[x]=max(ms[x],sz[v[i]]);
		}
	ms[x]=max(ms[x],ps-sz[x]);
	if(ms[rr]>ms[x])
		rr=x;
}
void d3(int x){
	vi[x]=1;
	for(int i=h[x];i;i=nxt[i])
		if(!vi[v[i]]){
			rr=0;
			d4(v[i],x);
			ps=sz[v[i]];
			gr(v[i],x);
			ff[rr]=x;
			d3(rr);
		}
}
void md(int x){
	mod(rt[x],0,1e8,0,1);
	int z=x;
	while(ff[z]){
		int d=dd(x,ff[z]);
		mod(rt[ff[z]],0,1e8,d,1);
		mod(rt[z+n],0,1e8,d,1);
		z=ff[z];
	}
}
int qu(int x,int va){
	int y=x,ans=qq(rt[x],0,1e8,0,va);
	while(ff[y]){
		int d=dd(x,ff[y]);
		if(va>=d)
			ans+=qq(rt[ff[y]],0,1e8,0,va-d)-qq(rt[y+n],0,1e8,0,va-d);
		y=ff[y];
	}
	return ans;
}
signed main(){
	freopen("treekth.in","r",stdin);
	freopen("treekth.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<n;i++){
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z);
		add(y,x,z);
	}
	ps=n;
	d1(1,0);
	d2(1,0,1);
	memset(sz,0,sizeof(sz));
	ms[0]=1e9;
	d4(1,0);
	gr(1,0);
	d3(rr);
	for(int i=1;i<=n;i++)
		md(i);
	for(int i=1;i<=n;i++){
		int k;
		scanf("%d",&k);
		int l=0,r=1e8,ans=0;
		while(l<=r){
			int md=(l+r)/2;
			if(qu(i,md)>=k){
				ans=md;
				r=md-1;
			}
			else
				l=md+1;
		}
		printf("%d
",ans);
	}
}
原文地址:https://www.cnblogs.com/ctmlpfs/p/14744782.html