[CERC2014] Parades 题解

感觉长脑子了。


考虑在路线两端点的 \(lca\) 计算贡献,那么线段可以分两类:

  1. \(u\)\(v\) 祖先。
  2. \(u,v\) 互不为祖先。

\(dp_i\) 表示只考虑 \(i\) 子树内的路线时的答案。

引理:若插入一条以 \(i\)\(lca\) 的路径会使以 \(i\) 的儿子为 \(lca\) 的路径数量减少,一定不优。

正确性显然。根据这一条性质,我们就可以不用维护儿子中具体的路径了,只需要维护 \(f_{i,j}\) 表示以 \(i\) 为根的子树中,能不能满足 \((i,j)\) 上没有边被使用且不使答案减小。

首先 \(i\) 的所有 \(1\) 类路径,能插就插,一定最优,没他一定不优。

其次剩下部分就是一般图最大匹配,\(dfs\) 即可,时间复杂度 \(O(sn^22^{sn})\)

考虑维护 \(f_{u,w}\)。假如 \(w\)\(u\) 的儿子 \(v\) 的子树中,那么 \(f_{u,w}=1\) 时,必定满足 \(f_{v,w}=1\)。那么问题转化为 \((u,v)\) 被占用是否是达成最优解的必要条件。这里的判断分两种情况:

  1. 能插入 \(1\) 类边。这种情况肯定被使用了。
  2. 能插入 \(2\) 类边。这种情况直接强制他不能插在这里,然后再跑一般图最大匹配,看答案有没有变化即可。

时间复杂度 \(O(tsn^32^{sn}n)\),由于题目保证 \(sn\le 10\),而且一般图最大匹配根本跑不满,因此可以通过。

#include<bits/stdc++.h>
#define pp pair<int,int>
using namespace std;
const int N=1005;
int t,n,m,dp[N],vs[N],f[N][N];
int idx[N][N],ft[N][15],dep[N];
vector<int>g[N],ux[N],uv[N];
vector<pp>xy[N],vw[N];
int maxp(int nm,int k,int nw){
	if(nm==g[k].size()) return nw;
	int re=maxp(nm+1,k,nw),x=g[k][nm];
	if(!vs[x]) for(int i=0;i<xy[k].size();i++){
		int v=vw[k][i].first,w=vw[k][i].second;
		int l=xy[k][i].first,r=xy[k][i].second;
		if(v>w) swap(l,r),swap(v,w);
		if(v!=x||vs[w]||!f[v][l]||!f[w][r]) continue;
		vs[v]=vs[w]=1,re=max(re,maxp(nm+1,k,nw+1)),vs[v]=vs[w]=0;
	}return re;
}void getf(int x,int fa,int u,int v,int id){
	f[u][x]=(id&f[v][x]);
	for(auto y:g[x])
		if(fa!=y) getf(y,x,u,v,id);
}void dfs(int x,int fa){
	sort(g[x].begin(),g[x].end());
	for(auto y:g[x])
		if(fa!=y) dfs(y,x),dp[x]+=dp[y];
	for(int i=0;i<ux[x].size();i++){
		int y=ux[x][i],v=uv[x][i];
		if(f[v][y]&&!vs[v]) vs[v]=1,dp[x]++;
	}int ans=maxp(0,x,0);
	for(auto y:g[x]){
		if(y==fa) continue;
		if(vs[y]){getf(y,x,x,y,0);continue;}
		vs[y]=1;int re=maxp(0,x,0);vs[y]=0;
		getf(y,x,x,y,re==ans);
	}dp[x]+=ans,f[x][x]=1;
}void get_fa(int x,int fa){
	ft[x][0]=fa,dep[x]=dep[fa]+1;
	for(int i=0;i<14;i++)
		ft[x][i+1]=ft[ft[x][i]][i];
	for(auto y:g[x])
		if(y!=fa) get_fa(y,x);
}int lca(int x,int y){
	for(int i=14;~i;i--)
		if(dep[ft[x][i]]>=dep[y]) x=ft[x][i];
	if(x==y) return x;
	for(int i=14;~i;i--)
		if(ft[x][i]!=ft[y][i]) x=ft[x][i],y=ft[y][i];
	return ft[x][0];
}void get_id(int x,int lst){
	if(x==1) return;
	idx[ft[x][0]][lst]=x;
	get_id(ft[x][0],lst);
}void solve(){
	for(int i=1;i<=n;i++){
		g[i].clear(),ux[i].clear(),uv[i].clear();
		xy[i].clear(),vw[i].clear(),dp[i]=vs[i]=dep[i]=0;
		for(int j=1;j<=n;j++) f[i][j]=idx[i][j]=0;
		for(int j=0;j<15;j++) ft[i][j]=0;
	}cin>>n;
	for(int i=1,x,y;i<n;i++)
		cin>>x>>y,g[x].push_back(y),g[y].push_back(x);
	cin>>m,get_fa(1,0);
	for(int i=1;i<=n;i++) get_id(i,i);
	for(int i=1;i<=m;i++){
		int x,y;cin>>x>>y;
		if(dep[x]<dep[y]) swap(x,y);
		int lc=lca(x,y);
		if(lc==y){
			uv[y].push_back(idx[y][x]);
			ux[y].push_back(x);continue;
		}vw[lc].push_back({idx[lc][x],idx[lc][y]});
		xy[lc].push_back({x,y});
	}dfs(1,0),cout<<dp[1]<<"\n";
}int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>t;
	while(t--) solve();
	return 0; 
}
posted @ 2024-12-20 22:04  长安一片月_22  阅读(9)  评论(0编辑  收藏  举报