题目链接:传送门
题目描述
中考考场有
N
N
N个房间和
N
−
1
N-1
N−1双向通道,任意两个房间均可到达。现在出了一件极其灵异的事情,监控摄像头的画面里,都响起了奇怪的声音。“嘎啦,嘎啦…”
考务主任决定加强监控,现在如果在某个房间中放一个监考员,则这个房间以及所有与这个房间相连的房间都会被监控.现在他想知道至少要多少监考员可以控制所有房间,以及有多少种不同的方案数。
但这究竟是什么声音呢?答案:女老师的高跟鞋 ¬_¬
输入格式
第一行一个数字
N
N
N,代表有
N
N
N个房间,房间编号从
1
1
1开始到
N
N
N。
N
≤
500000
N\leq 500000
N≤500000。下面将有
N
−
1
N-1
N−1行,每行两个数,代表这两个房间相连.
输出格式
第一行输出至少有多少个监考员才可以控制所有房间
第二行输出有多少种方案数,方案数会比较大,输出除以1032992941的余数。
比较显然的树形
d
p
dp
dp。
发现一维的子状态不能包括所有情况,所以考虑分类讨论。
为了方便,这里把一个房间被某个房间监控称为一个节点被某个节点“支配”。
d
p
[
i
]
[
0
]
dp[i][0]
dp[i][0]表示
i
i
i的子树内所有节点都被支配,且
i
i
i被自己支配的最少监考员数。
d
p
[
i
]
[
1
]
dp[i][1]
dp[i][1]表示
i
i
i的子树内所有节点都被支配,且
i
i
i被
i
i
i的孩子支配的最少监考员数。
d
p
[
i
]
[
2
]
dp[i][2]
dp[i][2]表示
i
i
i的子树内所有节点都被支配,且
i
i
i被
i
i
i的父亲支配,
i
i
i的孩子被孩子的孩子支配的最少监考员数(但是监考员数不把
i
i
i的父亲统计在内)
d
p
[
i
]
[
2
]
dp[i][2]
dp[i][2]比较难理解,推荐看成是
i
i
i的子树内除
i
i
i以外的所有节点都被支配,
i
i
i未被支配的最少监考员数(这样dp过程会更自然一些)。
c
n
t
[
i
]
[
0
/
1
/
2
]
cnt[i][0/1/2]
cnt[i][0/1/2]同理,表示的是方法数。
然后大莉跑分类讨论,跑树形dp即可qwq(具体见代码,内含详细注释)
毒瘤代码
#include<stdio.h>
#include<cstring>
#include<algorithm>
#define re register int
#define rl register ll
#define mod 1032992941
using namespace std;
typedef long long ll;
int read() {
re x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=10*x+ch-'0';
ch=getchar();
}
return x*f;
}
inline void write(int x) {
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int Size=500005;
const int INF=1e9;
namespace I_Love {
int n,cnt,head[Size];
struct Edge {
int v,next;
} w[Size<<1];
void AddEdge(int u,int v) {
w[++cnt].v=v;
w[cnt].next=head[u];
head[u]=cnt;
}
ll dp[Size][3];
ll num[Size][3];
void dfs(int x,int fa) {
dp[x][0]=1; //自己支配
dp[x][2]=0; //未被支配
dp[x][1]=INF; //孩子支配,不确定x有没有孩子,先设成INF
num[x][0]=num[x][1]=num[x][2]=1;
//如果x没有孩子,则dp[x][1]的值为INF,也就不可能为最优解
for(int i=head[x]; i; i=w[i].next) {
int nxt=w[i].v;
if(nxt!=fa) {
dfs(nxt,x);
//注意当多种转移方式结果相同时,方案数要相加qwq
//x由自己支配,孩子由谁支配都珂以
ll sum=0;
int val=min(dp[nxt][0],min(dp[nxt][1],dp[nxt][2]));
dp[x][0]+=val;
if(dp[nxt][0]==val) sum+=num[nxt][0];
if(dp[nxt][1]==val) sum+=num[nxt][1];
if(dp[nxt][2]==val) sum+=num[nxt][2];
num[x][0]=num[x][0]*sum%mod;
/**
* 1.x由之前的一个子节点支配,则孩子珂以由孩子自己支配或者由孩子的孩子支配
* 2.x还未被支配,孩子由孩子自己支配
* 第一种情况和第二种情况是并列的,要分开处理
*/
sum=0;
val=min(dp[x][1]+min(dp[nxt][0],dp[nxt][1]),dp[x][2]+dp[nxt][0]);
//Situation 1
if(dp[x][1]+dp[nxt][0]==val) sum+=num[nxt][0];
if(dp[x][1]+dp[nxt][1]==val) sum+=num[nxt][1];
num[x][1]=num[x][1]*sum%mod;
//Situation 2
if(dp[x][2]+dp[nxt][0]==val) num[x][1]=(num[x][1]+num[x][2]*num[nxt][0])%mod;
dp[x][1]=val;
/**
* x未被支配,则x之前一直未被支配,x的孩子也不能支配x
* 所以x的孩子只能被x的孩子的孩子支配
*/
dp[x][2]+=dp[nxt][1];
num[x][2]=num[x][2]*num[nxt][1]%mod;
}
}
}
void Kutori() {
n=read();
for(re i=1; i<n; i++) {
int u=read();
int v=read();
AddEdge(u,v);
AddEdge(v,u);
}
dfs(1,0);
//1号节点必须要被支配qwq
int ans=min(dp[1][0],dp[1][1]);
int sum=0;
if(dp[1][0]==ans) sum+=num[1][0];
if(dp[1][1]==ans) sum+=num[1][1];
printf("%d\n",ans);
printf("%d",sum%mod);
}
}
int main() {
I_Love::Kutori();
return 0;
}