传送门
题目描述:
有一张无向联通图 G=⟨V,E⟩ ,其中顶点数 |V|=n ,边数 |E|=n−1 。求有多少种方案使得删边后残余图中的最大匹配数恰好为 m 的倍数。
题解:
这道题看起来是求最大匹配,其实关系不大,正解是树形DP
定义一个数组dp[i][j][k], 其中
i表示节点,
j表示i的子树中的最大匹配数%m,
k表示i是否与其子节点匹配.
接下来推一下合并两颗子树u,v的递推式即可
注意几个细节:
1.用快读,否则会TLE
2.尽量少取余,否则会很慢
3.有2n条边,我一开始就因为数组开小了卡了很久
4.dp[i][j][k]上一次的结果不带入这一次,要另开一个tmp数组来执行操作
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
inline int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();} return x*f;
}
const int N=50010;
const int M=200;
const int mod=998244353;
int n,m;
struct Edge{int v,nxt;
}edge[N<<1];
ll dp[N][M][2],tmp[M][2];
int head[N],cnt,fa[N],sz[N];
void add_edge(int u,int v){edge[++cnt].v=v;edge[cnt].nxt=head[u];head[u]=cnt;
}
void dfs(int u){sz[u]=1;dp[u][0][0]=1;for(int i=head[u];i;i=edge[i].nxt){int v=edge[i].v;if(v==fa[u]) continue;fa[v]=u;dfs(v);for(int i=0;i<m;i++) tmp[i][0]=tmp[i][1]=0;for(int j=0;j<=sz[u]&&j<m;j++){for(int k=0;k<=sz[v]&&k<m;k++){int v1=(j+k+1)%m,v2=(j+k)%m;tmp[v1][1]+=dp[u][j][0]*dp[v][k][0];tmp[v1][1]%=mod;tmp[v2][0]+=dp[u][j][0]*dp[v][k][0]+dp[u][j][0]*dp[v][k][1]*2;tmp[v2][0]%=mod;tmp[v2][1]+=dp[u][j][1]*dp[v][k][0]*2+dp[u][j][1]*dp[v][k][1]*2;tmp[v2][1]%=mod;}}for(int i=0;i<m;i++){dp[u][i][0]=tmp[i][0];dp[u][i][1]=tmp[i][1];}sz[u]+=sz[v];}
}
int main(){n=read();m=read();for(int i=1;i<n;i++){int u=read();int v=read();add_edge(u,v);add_edge(v,u);}dfs(1);printf("%d\n",(dp[1][0][0]+dp[1][0][1])%mod);return 0;
}