CF1326G 题解

题意:

蛛网树是一颗平面树,满足点是该树的凸包的顶点上等价于其是叶子。

给定一个平面树,求有多少种对点集的划分,使得每个划分出来的集合都是蛛网树。

Solution

考虑树形 dp。设 \(f_u\)\(u\) 子树内的划分方案。先考虑两种特殊情况,以 \(u\) 为最浅点的蛛网树只有 \(1,2\) 个点,是容易转移的(因为后面的凸包转移是按边所以这些不会统计到)。

先考虑一个凸包带来的贡献最后应当计算所有凸包带权的和累加到 f 上。设凸包为 \(S\),其带的权应为:

\[\prod_{u\not\in S,fa_u\in S}f_u \]

考虑拆一下贡献。

把到叶子的线延长划分出若干区域。把每个区域内的贡献乘起来就可以了。

中间的那些点一定是在树上的路径(判断时有一些细节,不是半平面交起来!)。直接乘起来即可。这样,就解决了计算权值的问题。

还需要知道:哪些点对有贡献?即哪些点对可能成为相邻的叶子?显然,树上路径点必须在连边的一侧(不妨统一设为左侧),然后根据原树上边转移才能保证没有问题。具体来说,不是按照叶子转移,而是先对子树内的边标号,双向边两个方向不同,然后按照相邻叶子在原树上的起始边转移。

这里的转移类似于 P2924 的转移,先把向量排序,再枚举开始的边和按顺序枚举向量转移,叠加答案即可(注意取消掉只有自己的贡献)。

A 是排序后的向量集合。bh 是边的编号,P 向量的 d 是权值,F,S 是开始边和终止边。x,y 是枚举的起点边。这样的起点边当然必须在某个选定象限内。

for(int i=1;i<=tot;i++)gg[i]=0;
gg[bh[x][y]]=1;
for(auto P:A)(gg[P.F]+=gg[P.S]*P.d)%=mod;
(gg[bh[x][y]]+=mod-1)%=mod;
(f[u]+=gg[bh[x][y]])%=mod;

这样的转移显然是 \(O(n^3)\) 的。

为什么不能按点?可以看四号样例的图:

按照原理来讲,不应该有下面这个凸包:

但是按点的统计就不能避免这种情况。根本原因是,点转移不能区分一个点是否被真正当成叶子,像这个 A 点就连了两条边。容易发现按边就不会出任何问题。

最后还有一个问题:这个凸包必须存在一个相邻点对,满足他们在树上的路径包含当前处理 f 数组的 \(u\),实际上就是 \(u\) 在凸包叶子的虚树上。这个很容易保证,dp 时搞一下或者容斥一下均可。

代码在处理权值的时候参考了 std(因为之前一直写的是在半平面交内还能过 21 个点……)。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=105,mod=998244353;
struct pt{int x,y;}p[maxn];
pt operator -(pt a,pt b){return {a.x-b.x,a.y-b.y};}
int operator ^(pt a,pt b){return a.x*b.y-a.y*b.x;}
int operator *(pt a,pt b){return a.x*b.x+a.y*b.y;}
int f[maxn],val[maxn][maxn];
#define VI  vector<int>
VI lft[maxn][maxn],e[maxn],cur,Ares,E[maxn];
int n,T,dep[maxn];
void dfs(int u,int fa){
	cur.push_back(u);
	if(u==T)Ares=cur;
	for(auto v:E[u]){
		if(v==fa)continue;
		dfs(v,u);
	}
	cur.pop_back();
}
VI getpath(int x,int y){
	cur.clear();T=y;dfs(x,0);
	return Ares;
}
bool check(pt a,pt b,pt c){
	return ((b-a)^(c-a))>0;
}
bool good[maxn][maxn];
void dfs2(int u){
	cur.push_back(u);
	for(auto v:e[u])dfs2(v);
}
VI getsubt(int u){
	cur.clear();
	dfs2(u);
	return cur;
}
int g[maxn];
struct vec{int u,v,d,S,F,ex;};
bool pd(pt a,pt b){
	return 1-(b.y>a.y||(b.y==a.y&&b.x>=a.x));
}
bool pd(pt a){
	return pd((pt){0,0},a);
}
bool operator <(vec a,vec b){
	pt A=p[a.v]-p[a.u],B=p[b.v]-p[b.u];
	if(pd(A)!=pd(B))return pd(A)<pd(B);
	return (A^B)>0;
}
int Find(int u,const VI &A){
	for(auto v:A)if(v==u)return 1;
	return 0;
}
bool fat(int x,int y){
	VI chain=getpath(x,y);
	if(chain.size()==abs(dep[x]-dep[y])+1)return 1;
	return 0;
}
bool pd2(pt a,pt b){
	if((a^b)!=0)return (a^b)<0;
	return (a*b)<0;
}
bool PD(pt a,pt b,pt c){
	int x=pd2(a,b),y=pd2(a,c);
	if(x!=y)return x<y;
	return (b^c)>0;
}
bool dm[maxn][maxn],ckd[maxn][maxn];
vector<int> G[maxn];
int bh[maxn][maxn],gg[maxn*maxn];
void dp(int u){
	int prod=1;
	for(auto v:e[u]){
		dp(v);
		(prod*=f[v])%=mod;
	}
	f[u]=prod;
	for(auto v:e[u]){
		int p=1;
		for(auto w:e[v])(p*=f[w])%=mod;
		for(auto v2:e[u])if(v2!=v)(p*=f[v2])%=mod;
		(f[u]+=p)%=mod;
	}
	VI sub=getsubt(u);
	for(auto u:sub)G[u].clear();
	int tot=0;
	for(auto u:sub){
		for(auto v:e[u]){
			G[u].push_back(v);
			G[v].push_back(u);
			bh[u][v]=++tot;
			bh[v][u]=++tot;
		}
	}
	vector<vec> A;
	for(auto x:sub){
		for(auto y:sub){
			if(x==y||!good[x][y]||dm[x][y])continue;
			if(((p[y]-p[x])^(p[u]-p[x]))<0)continue;
			VI path=getpath(x,y);int N=path.size()-1,d=1;
			for(int i=0;i<=N;i++){
				int v=path[i];
				pt A,B;
				if(i==0)A=p[v]-p[path[i+1]];
				else A=p[path[i-1]]-p[v];
				if(i==N)B=p[v]-p[path[i-1]];
				else B=p[path[i+1]]-p[v];
				for(auto w:e[v]){
					if(!PD(A,p[w]-p[v],B))continue;
					if((i>0&&w==path[i-1])||(i<N&&w==path[i+1]))continue;
					(d*=f[w])%=mod;
				}
			}
			bool tp=0;
			if(Find(u,path))tp=1;
			A.push_back({x,y,d,bh[path[0]][path[1]],bh[path[N]][path[N-1]],tp});
		}
	}
	sort(A.begin(),A.end());
	for(auto x:sub){
		for(auto y:G[x]){
			if(pd(p[x],p[y]))continue;
			for(int i=1;i<=tot;i++)gg[i]=0;
			gg[bh[x][y]]=1;
			for(auto P:A)(gg[P.F]+=gg[P.S]*P.d)%=mod;
			(gg[bh[x][y]]+=mod-1)%=mod;
			(f[u]+=gg[bh[x][y]])%=mod;
		}
	}
	for(auto x:sub){
		for(auto y:G[x]){
			if(pd(p[x],p[y]))continue;
			for(int i=1;i<=tot;i++)gg[i]=0;
			gg[bh[x][y]]=1;
			for(auto P:A)if(P.ex==0)(gg[P.F]+=gg[P.S]*P.d)%=mod;
			(gg[bh[x][y]]+=mod-1)%=mod;
			(f[u]+=mod-gg[bh[x][y]])%=mod;
		}
	}
}
int fa[maxn];
void dfs1(int u,int f){
	fa[u]=f;dep[u]=dep[f]+1;
	for(auto v:e[u]){
		if(v==f)continue;
		dfs1(v,u);
	}
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++)cin>>p[i].x>>p[i].y;
	for(int i=1;i<n;i++){
		int u,v;cin>>u>>v;
		e[v].push_back(u);
		e[u].push_back(v);
		E[u].push_back(v);
		E[v].push_back(u);
		dm[u][v]=dm[v][u]=1;
	}
	dfs1(1,0);
	for(int i=2;i<=n;i++){
		VI A;
		for(auto u:e[i])if(u!=fa[i])A.push_back(u);
		swap(A,e[i]);
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(i==j||dm[i][j])continue;
			VI chain=getpath(i,j);
			bool ck=1;
			for(auto u:chain)
				if(u!=i&&u!=j&&!check(p[i],p[j],p[u])){
					ck=0;break;
				}
			good[i][j]=ck;
		}
	}
	dp(1);
	cout<<f[1]<<endl;
	return 0;
}
posted @ 2024-05-29 09:18  British_Union  阅读(7)  评论(0编辑  收藏  举报