题解P4201: [NOI2008]设计路线

发现给出了一棵树, 不是树的情况直接输出-1

考虑进行DP, 设f[i][0/1/2]为i的子树中选小于等于0/1/2条边修路的方案数, 不妨对于一个节点, 先考虑正好相等的情况, 假设当前扫到了一个节点v, 则有

\[f[i][0] = \max\{f[i][0]\, f[v][2]+1\} \\ f[i][1] = \min\{\max\{f[i][1], f[v][2]+1\}, \max\{f[i][0], f[v][1]\}\} \\ f[i][2] = \min\{\max\{f[i][2], f[v][2]+1\}, \max\{f[i][1], f[v][1]\}\} \]

接下来前缀min一下即可, 注意到要2->1->0更新, 并且要前缀min

接下来考虑DP出方案数, 发现我们所求的f[1][2]的最大值是\(O(log_3 n) \leq 11\)的, 因此设计状态时要把这个作为一个维度

咕咕咕

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define _ 100005
#define rep for(int t=0; t<=f[1][2]; ++t)
int f[_][3], g[_][20][3], inf=0x3f3f3f3f;
int Next[_<<1], ver[_<<1], head[_], tot;
int n, m, q;
int gv(int x, int y, int z){
	if(x>=0 && y>=0 && z>=0) return g[x][y][z]; return 0;
}
void dfs1(int u, int fa){
	f[u][0] = 0, f[u][1] = inf, f[u][2] = inf;
	for(int i=head[u]; i; i=Next[i]){
		int v=ver[i]; if(v == fa) continue; dfs1(v, u);
		f[u][2] = min(max(f[u][2], f[v][2]+1), max(f[u][1], f[v][1]));
		f[u][1] = min(max(f[u][1], f[v][2]+1), max(f[u][0], f[v][1]));
		f[u][0] = max(f[u][0], f[v][2]+1);
	}
	f[u][1] = min(f[u][0], f[u][1]); f[u][2] = min(f[u][2], f[u][1]);
}
void dfs2(int u, int fa){
	rep g[u][t][0]=1, g[u][t][1]=g[u][t][2]=0;
	for(int i=head[u]; i; i=Next[i]){
		int v=ver[i]; if(v == fa) continue; dfs2(v, u);
		rep {
			g[u][t][2] = gv(u, t ,2)*gv(v, t-1, 2) + gv(u, t, 1)*gv(v, t, 1); g[u][t][2]%=q;
			g[u][t][1] = gv(u, t, 1)*gv(v, t-1, 2) + gv(u, t, 0)*gv(v, t, 1); g[u][t][1]%=q;
			g[u][t][0] = gv(u, t, 0)*gv(v, t-1, 2); g[u][t][0]%=q;
		}
	}
	rep (g[u][t][1]+=g[u][t][0])%=q, (g[u][t][2]+=g[u][t][1])%=q;
}
void add(int u, int v){
	ver[++tot]=v, Next[tot]=head[u], head[u]=tot;
}
signed main(){
	scanf("%lld%lld%lld", &n, &m, &q);
	if(m != n-1) return (puts("-1"), puts("-1"), 0);
	for(int i=1; i<=m; ++i){
		int x, y; scanf("%lld%lld", &x, &y); add(x, y); add(y, x);
	}
	dfs1(1, 0); dfs2(1, 0);
	printf("%lld\n%lld\n", f[1][2], gv(1, f[1][2], 2));
}
posted @ 2019-12-29 22:48  tyqtyq~!  阅读(138)  评论(0编辑  收藏  举报