题解:P4434 [COCI 2017/2018 #2] ​​Usmjeri

目前暂无修正。

前言:我最近怎么写了这么多简单问题复杂化的题解???

前置知识:扩展域并查集、树形 DP、离线二维数点(不必要?)。

水个题解,写一个常数大、码量大、难写、简单问题复杂化的思路。时间复杂度 \(O(n\log n+(n+m)\alpha(n))\)

假定树是以 \(1\) 为根的有根树,我们让边权为 \(0\) 表示向下,\(1\) 表示向上。首先设 \(f_{u,0/1}\) 表示以 \(u\) 为根的子树中,\(u\) 与其父亲 \(fa\) 连边权值为 \(0/1\) 的方案数。

考虑转移,发现在输入节点对 \((a_i,b_i)\) 时可以把它挂到 \(lc=\text{LCA}(a_i,b_i)\) 上处理,若 \(lc\neq a_i\land lc\neq b_i\),我们找到 \(lc\) 包含 \(a_i,b_i\) 的子树树根分别记为 \(A,B\),则当 DP 处理 \(lc\)\(f_{A,0}\) 只能和 \(f_{B,1}\) 结合,\(f_{A,1}\) 只能和 \(f_{B,0}\) 结合。

同理,我们发现 \(a_i\to A,b_i\to B\) 这两条链上边权必须都相等。于是我们考虑能否在处理 \(lc\) 时找到在与 \(lc\) 其他子树的点匹配后仍有剩余点的子树。

这个东西我不会求所以糊了个离线二维数点上去,通过 DFS 序和离线二维数点找出 \(v\)\(u\) 匹配后消掉了多少个点 \(cut_v\),然后再用树上差分算出 \(v\) 子树内原来剩余未匹配点数 \(cnt_v\),如果 \(cnt_v-cut_v>0\) 就存在剩余。

(具体实现:\((a_i,b_i),(b_i,a_i)\) 单点加,\(cut_v\)\(x\in[dfn_v,dfn_v+siz_v-1]\land y\in[dfn_u,dfn_u+siz_u-1]\text{ 的 }(x,y)\text{ 数量}-x\in[dfn_v,dfn_v+siz_v-1]\land y\in[dfn_v,dfn_v+siz_v-1]\text{ 的 }(x,y)\text{ 数量}\)

找到的这些 \(v\) 子树一定有 \(f_{v,0}\) 转移到 \(f_{u,0}\)\(f_{v,1}\) 转移到 \(f_{u,1}\)。不妨对每个点设一个状态 \((v,0)\) 代表 \(f_{v,0}\)\((v,1)\) 代表 \(f_{v,1}\),并查集维护连通性,在一个连通块内才能相互结合。只需一开始找到挂在 \(lc\) 上的 \(A,B\) 并连边 \((A,0)-(B,1),(A,1)-(B,0)\)

对于仍有剩余点的子树 \(v_i\) 把所有 \((v_i,0)\) 串起来,\((v_i,1)\) 串起来即可。每个连通块 \(a_X\) 的贡献就是其点权乘积。\(f_{u,0/1}\)\(f_{v_i,0/1}\) 分别强制捆绑,然后每次找到一对连通块 \(X,Y\)(因为连边是对称的),其对 \(f_{u,0/1}\) 都有贡献 \(a_X+a_Y\)。直接转移即可。

最后答案是 \(f_{1,0}\)\(f_{1,1}\),代码不建议参考,仅提供一个构式的思路。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL MOD=1e9+7;
const int N=3e5+5;
int n,m,son[N],dep[N],dfn[N],tms;
int top[N],fat[N],siz[N];
int unc[N],fa[N*2],cut[N];
LL f[N][2];
struct BIT{
	int av[N];
	inline int lowbit(int x){return x&-x;}
	void add(int p,int x){for(int i=p;i<=n;i+=lowbit(i))av[i]+=x;}
	int que(int p){int res=0;for(int i=p;i;i-=lowbit(i)){res+=av[i];}return res;}
}T;
struct Q{int l,r,id;};
vector<int>G[N],Add[N];
vector<Q>q[N];
inline int fr(int x){return fa[x]==x?x:fa[x]=fr(fa[x]);}
void ins(int x,int y){
	int frx=fr(x),fry=fr(y);
	if(frx==fry)return ;
	fa[frx]=fry;
}
void dfs0(int u,int fa){
	dep[u]=dep[fa]+1;
	siz[u]=1;fat[u]=fa;
	for(int v:G[u]){
		if(v==fa)continue;
		dfs0(v,u);
		siz[u]+=siz[v];
		if(siz[v]>siz[son[u]])
			son[u]=v;
	}
}
void dfs1(int u){
	dfn[u]=++tms;
	int fa=fat[u];
	if(son[fa]==u)top[u]=top[fa];
	else top[u]=u;
	if(son[u])dfs1(son[u]);
	for(int v:G[u]){
		if(v==fa||v==son[u])continue;
		dfs1(v);
	}
}
int LCA(int x,int y){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		x=fat[top[x]];
	}
	if(dep[x]>dep[y])swap(x,y);
	return x;
}
int jump(int x,int y){
	int last=0;
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		last=top[x],x=fat[top[x]];
	}
	if(dep[x]>dep[y])swap(x,y);
	if(x==y)return last;
	return son[x];
}
bool vis[N*2];
void dfs(int u){
	int fa=fat[u],ex=0;
	for(int v:G[u]){
		if(v==fa)continue;
		dfs(v);unc[u]+=unc[v];
		if(unc[v]-cut[v]>0)ex=v;
	}
	if(!son[u])f[u][0]=1,f[u][1]=1;
	else {
		bool tf=1;
		for(int v:G[u]){
			if(v==fa)continue;
			if(fr(v)==fr(v+n)){tf=0;break;}
			if(unc[v]-cut[v]>0)ins(v,ex),ins(v+n,ex+n);
		}
		if(!tf){f[u][0]=f[u][1]=0;return ;}
		for(int v:G[u]){
			if(v==fa)continue;
			if(fr(v)>n)
				f[fr(v)-n][1]=f[fr(v)-n][1]*f[v][0]%MOD;
			else if(fr(v)!=v)
				f[fr(v)][0]=f[fr(v)][0]*f[v][0]%MOD;
			if(fr(v+n)<=n)
				f[fr(v+n)][0]=f[fr(v+n)][0]*f[v][1]%MOD;
			else if(fr(v+n)!=v+n)
				f[fr(v+n)-n][1]=f[fr(v+n)-n][1]*f[v][1]%MOD;
		}
		int frex=0,frexn=0;
		if(ex){
			frex=fr(ex),frexn=fr(ex+n);
			vis[frex]=vis[frexn]=1;
			f[u][0]=(frex<=n?f[frex][0]:f[frex-n][1]);
			f[u][1]=(frexn<=n?f[frexn][0]:f[frexn-n][1]);
		}
		else f[u][0]=f[u][1]=1;
		for(int v:G[u]){
			if(v==fa)continue;
			assert(!(vis[fr(v)]^vis[fr(v+n)]));
			if(vis[fr(v)]||vis[fr(v+n)])continue;
			vis[fr(v)]=vis[fr(v+n)]=1;
			int a=fr(v),b=fr(v+n);
			bool ga=0,gb=0;
			if(a>n)a-=n,ga=1;
			if(b>n)b-=n,gb=1;
			f[u][0]=f[u][0]*(f[a][ga]+f[b][gb])%MOD;
			f[u][1]=f[u][1]*(f[a][ga]+f[b][gb])%MOD;
		}
	}
}
int main(){
	//freopen("dispatch.in","r",stdin);
	//freopen("dispatch.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=2*n;i++)
		fa[i]=i;
	for(int i=1;i<n;i++){
		int u,v;scanf("%d%d",&u,&v);
		G[u].emplace_back(v);
		G[v].emplace_back(u); 
	}
	dfs0(1,0);dfs1(1);
	for(int i=2;i<=n;i++){
		int v=i,u=fat[v];
		q[dfn[v]-1].emplace_back(Q{dfn[u],dfn[u]+siz[u]-1,-v});
		q[dfn[v]+siz[v]-1].emplace_back(Q{dfn[u],dfn[u]+siz[u]-1,v});
		q[dfn[v]-1].emplace_back(Q{dfn[v],dfn[v]+siz[v]-1,v});
		q[dfn[v]+siz[v]-1].emplace_back(Q{dfn[v],dfn[v]+siz[v]-1,-v});
	}
	for(int i=1;i<=m;i++){
		int a,b;
		scanf("%d%d",&a,&b);
		Add[dfn[a]].emplace_back(dfn[b]);
		Add[dfn[b]].emplace_back(dfn[a]);
		int lc=LCA(a,b);
		unc[a]++;unc[b]++;
		unc[lc]-=2;
		if(lc!=a&&lc!=b){
			a=jump(a,lc);
			b=jump(b,lc);
			ins(a,b+n);
			ins(b,a+n);
		}
	}
	for(int i=1;i<=n;i++){
		for(int v:Add[i])
			T.add(v,1);
		for(Q v:q[i]){
			if(v.id>0)cut[v.id]+=T.que(v.r)-T.que(v.l-1);
			else cut[-v.id]-=T.que(v.r)-T.que(v.l-1);
		}
	}
	dfs(1);
	printf("%lld",f[1][0]);
	return 0;
}
posted @ 2025-10-27 15:44  TBSF_0207  阅读(10)  评论(4)    收藏  举报