[Tricks-00009]bzoj3592 平面图剖分杂谈

可能会写不太清楚,见谅。

首先离线下来,把题目中的所有边都在二维平面上画出来,则一定是一个边双连通的平面剖分图。也就是,边都是线段的平面图,而且坐标都给好了。你可以通过经典做法(对所有点相邻的边极角排序,找出所有面建出对偶图,建个生成树)就可以刻画多边形内部所有面的性质了。

不过这道题它不!需!要!

为什么呢?因为题目只需要刻画点,没必要把面算完之后再绕回来去计算点。因此,关键的是,我们只需要求出一棵点生成树,根节点可以取一个确定在凸包上的点(例如,\(y\) 坐标最小的同时 \(x\) 坐标也要在其中最小,即只要达成这样的目标:最后走到根时不能还在一个多边形内部)。接下来,我们仿照传统的判点是否在多边形内的方法,给出一个针对平面图,更可爱的做法(其实是从 CF223E 学来的):

原本是从一点引出一条射线,判断交出的点数量是奇数还是偶数。实际上,我们没必要要求它是射线!任意一条折线其实都是 ok 的,也就是说,最方便的找法就是生成树上,该点到根的所有,把它当作有向,从下往上。那么我们可以把从环上点走到环内点的边的贡献减 \(1\),从环内点走向环上点的加 \(1\)。这样你就会注意到,一个点在环内当且仅当这条路径上的权值和为 \(1\),不在就为 \(0\)。比如只去统计环内点权和的话,实际上环上的点给的贡献是独立的。如果你对树上每个点的儿子做了极角排序,则可以直接通过对极角二分来求出对应的区间,转成了至多两个 dfs 序上的连续段!

哦对了,刚才极角二分为什么是有道理的,给个说法:注意到,本题保证了给出的多边形是顺时针的,我不清楚这里顺时针怎么定义?不过有个判定是,叉积算面积后的值为负数。然后顺时针的性质似乎是,考虑一个顶点和它在多边形中连向的两条边,该点在其它时刻连出的边是否在多边形中,只要看极角是否在两条边对应的极角的有向区间内。因此,只要分别处理 \(L\leq R\)\(L>R\) 的情况就好了。

回到原题,此时第一问已经做完了,但是看起来第二问不能通过直接拆贡献算。所以我们考虑换个思路。

这里就不唠闲嗑绕圈子了,直接给出最后结论:设多边形点数为 \(k\),则所有合法点在生成树极角排序后的 dfs 序上构成 \(O(k)\) 个连续段。

不过这也是好证明的。因为我们刚才那个做法,其实你先不要立刻算答案,就把权值挂在点上,+1/-1 和 ^1 是没区别的。因此就相当于,考虑序列差分,偶数段会有贡献,把这些拿出来就好了。

于是题目就变成了单点修改区间求和求最大值的问题,线段树维护十分轻松,复杂度一只 log,跑得不算慢。

其实还可以扩展到单边修改,多边形内所有边查询信息,有兴趣的读者可以思考一下,不难的。

给个代码:

#include<bits/stdc++.h>
using namespace std;
int x[50005],y[50005],d[50005];
int xx[50005],dd[50005],op[50005],m[50005];
vector<int>gg[50005],g[50005];
struct fenshu{
	long long a,b;
	fenshu(long long a=0,long long b=0):a(a),b(b){}
	bool operator<(const fenshu &other)const{
		return (__int128)a*other.b<(__int128)other.a*b;
	}
};
fenshu getpi(int x,int y){
	if(y>=0)return fenshu((x>0?1ll:-1ll)*x*x,1ll*x*x+1ll*y*y);
	return fenshu(-(x>0?1ll:-1ll)*x*x-7*(1ll*x*x+1ll*y*y),1ll*x*x+1ll*y*y);
}
int vist[50005],fa[50005];
vector<pair<fenshu,int>>g2[50005];
void dfs0(int x,int la){
	vist[x]=1;
	for(auto cu:g[x]){
		if(cu==la)continue;
		if(vist[cu])continue;
		fa[cu]=x;
		dfs0(cu,x);
	}
}
int bg[50005],en[50005],tot=0;
void dfs1(int x){
	bg[x]=en[x]=++tot;
	for(auto pi:g2[x]){
		int cu=pi.second;
		dfs1(cu);
		en[x]=en[cu];
	}
}
long long a[50005];
int pi[1000005],tt;
void jia(int l,int r,int x){
	if(x==1||x==-1){
		pi[++tt]=l;pi[++tt]=r+1;
	}
}
long long ha,ma;
long long sm[200005],s2[200005];
void build(int l,int r,int o){
	if(l==r){
		sm[o]=s2[o]=a[l];
		return;
	}
	int mid=(l+r)>>1;
	build(l,mid,o<<1);build(mid+1,r,o<<1|1);
	sm[o]=sm[o<<1]+sm[o<<1|1];s2[o]=max(s2[o<<1],s2[o<<1|1]);
}
void gai(int l,int r,int o,int x){
	if(l==r){
		sm[o]=s2[o]=a[l];
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid)gai(l,mid,o<<1,x);
	else gai(mid+1,r,o<<1|1,x);
	sm[o]=sm[o<<1]+sm[o<<1|1];s2[o]=max(s2[o<<1],s2[o<<1|1]);
}
void query(int l,int r,int o,int ll,int rr){
	if(l>=ll&&r<=rr){
		ha+=sm[o];ma=max(ma,s2[o]);
		return;
	}
	int mid=(l+r)>>1;
	if(mid>=ll)query(l,mid,o<<1,ll,rr);
	if(mid<rr)query(mid+1,r,o<<1|1,ll,rr);
}
int main(){
	int ca;
	scanf("%d",&ca);
	int n;
	scanf("%d",&n);
	int my=INT_MAX,mx=INT_MAX,wz=0;
	for(int i=1;i<=n;++i){
		scanf("%d%d",&x[i],&y[i]);
		scanf("%d",&d[i]);
		if(my>y[i]||(my==y[i]&&mx>x[i]))my=y[i],mx=x[i],wz=i;
	}
	int q;
	scanf("%d",&q);
	for(int i=1;i<=q;++i){
		char oo[15];
		scanf("%s",oo+1);
		if(oo[1]=='H'){
			op[i]=1;
			scanf("%d%d",&xx[i],&dd[i]);
		}else{
			op[i]=2;
			scanf("%d",&m[i]);
			gg[i].resize(m[i]);
			for(int j=0;j<m[i];++j){
				scanf("%d",&gg[i][j]);
			}
			for(int j=0;j<m[i];++j){
				int u=gg[i][j],v=gg[i][(j+1)%m[i]];
				g[u].emplace_back(v);
				g[v].emplace_back(u);
			}
		}
	}
	dfs0(wz,0);
	for(int i=1;i<=n;++i)if(i!=wz){
		g2[fa[i]].emplace_back(getpi(x[i]-x[fa[i]],y[i]-y[fa[i]]),i);
	}
	for(int i=1;i<=n;++i){
		sort(g2[i].begin(),g2[i].end());
	}
	dfs1(wz);
	for(int i=1;i<=n;++i)a[bg[i]]=d[i];
	build(1,n,1);
	for(int i=1;i<=q;++i){
		if(op[i]==1){
			a[bg[xx[i]]]+=dd[i];
			gai(1,n,1,bg[xx[i]]);
		}else{
			ha=0,ma=0;
			int k=m[i];tt=0;
			reverse(gg[i].begin(),gg[i].end());
			for(int j=0;j<k;++j){
				int u=gg[i][j],v=gg[i][(j+1)%k],w=gg[i][(j+2)%k];
				ha+=a[bg[v]];ma=max(ma,a[bg[v]]);
				fenshu L=getpi(x[u]-x[v],y[u]-y[v]),R=getpi(x[w]-x[v],y[w]-y[v]);
				int xl=lower_bound(g2[v].begin(),g2[v].end(),make_pair(L,0))-g2[v].begin();
				int xr=lower_bound(g2[v].begin(),g2[v].end(),make_pair(R,n+1))-g2[v].begin()-1;
				if(!(R<L)){
					if(xl<=xr){
						jia(bg[g2[v][xl].second],en[g2[v][xr].second],1);
					}
					if(fa[v]){
						fenshu ff=getpi(x[fa[v]]-x[v],y[fa[v]]-y[v]);
						if(ff<L||R<ff);
						else{
							jia(bg[v],en[v],-1);
						}
					}
				}else{
					if(xl<(signed)g2[v].size()){
						jia(bg[g2[v][xl].second],en[g2[v][g2[v].size()-1].second],1);
					}
					if(xr>=0){
						jia(bg[g2[v][0].second],en[g2[v][xr].second],1);
					}
					if(fa[v]){
						fenshu ff=getpi(x[fa[v]]-x[v],y[fa[v]]-y[v]);
						if(ff<L&&R<ff);
						else{
							jia(bg[v],en[v],-1);
						}
					}
				}
			}
			sort(pi+1,pi+tt+1);
			for(int j=2;j<=tt;j+=2){
				int L=pi[j-1],R=pi[j]-1;
				if(L<=R)query(1,n,1,L,R);
			}
			printf("%lld %lld\n",ha,ma);
		}
	}
	return 0;
}
posted @ 2025-03-15 08:27  maihe  阅读(54)  评论(0)    收藏  举报