您的位置:首页 > 其它

[jzoj]3482. 【NOIP2013模拟10.23】轮舞前夕(经典树形DP)

2017-10-06 20:36 1121 查看
https://jzoj.net/senior/#contest/show/2150/2

一道很坑的DP题.

Description

「寻找希望…为了找出希望…

那才是真正的希望…

只要拥有这样的希望,

不管陷入怎样的困境,我们都能继续前行…」

“这是 发生在互相残杀的学园生活之前的故事 。也或许根本就是某个平行世界的故事。

“黑幕站在大门口,静静地审视着希望之峰学园 。旁边站着的是一只 长得像玩偶的熊, 身体一半是黑色一半是白。 不知道是否对接下来将要引起的绝望而感到兴奋,黑幕的嘴角不易察觉地微微上扬。 而身旁 的黑白熊则 ‘唔噗噗噗 ’地意味不明地笑着。

“为了 计划的顺利实施,黑幕需要在学园内装上能监视到所有地方的摄像头。 学园 里一共 有n个房间,有一些房间通过走廊相连。出于特殊的原因,学园只修建了保证所有房间都能互相到达的前提下的最少的走廊。一个房间里的摄像头可以监视到这个房间以及这个房间直接通过走廊相连的所有房间。

“配置摄像头也不是一件简单的事情,所以黑幕决定安装尽量少摄像头。 同时黑幕也想到了一个超高校级的问题: 在安装最少的摄像头前提下,一共有多少种安装的方案? 两个方案不同当且仅存在一个房间在种方案中安装了摄像头,而在另一种方案中没有安装。

“答案 我已经计算出来了哦,你能吗? ”Alter Ego对着屏幕前的人,微笑着说道。

Input

输入 第一行含有一个整数n,代表学园里房间的数量。

接下来n−1行,每行描述一条走廊。每行含有 两个数,代表这条走廊连接的两个房间。

Output

输出 一共两行 。

第一行输出 一个整数,代表需要安装的最少摄像头的数目。

第二行输出一个整数,代表安装的方案数。由于答案可能很大,故输出答案对1,000,000,007取模的结果。

Sample Input

7

2 1

3 1

4 2

5 1

6 2

7 6

Sample Output

3

4

Solution

这是一道比较经典的DP题.

一开始的想法:f[i][0/1]表示第i个点是否选.

后来发现,这样子并不能处理孙子.

于是,题解的经典设法就是:

f[i]表示第i个点选择.

g[i]表示第i个点不选,且儿子中必有一个选.

h[i]表示第i个点及其所有儿子都不选,但父亲选.

上面三个状态都必须保证以i为根的子树得合法.

很显然,当i为叶子节点时g[i]=∞,表示这个在状态不合法.

下面,因为这道题的g[i]比较难推,所以我们详细讲一下g[i]如何求.

g[i]=min{f[k]+∑j∈son{i}且j≠kmin{f[j],g[j]}}

具体的理解就是,因为要求一个点必须选,那么我就选择一个点,保证当前第i个点,一定被覆盖,其余就可以选最小了.

我们发现,这样更新是O(n2)的,有人说可以再扫一遍,然后求就行了呀.

但其实我们可以连再扫一遍都不洗要,直接求得g[i].

具体方法就是:求出min{f[j]−g[j]}=w,下面分三步讨论.

如果w<0,那么任意一个满足f[j]<g[j]的,都是最优方案,直接记录.

如果w=0,那么任意一个满足f[j]=g[j]的,也是最优方案,直接记录.

如果w>0,那么当满足f[j]−g[j]=w的,就是最优方案,直接记录.

第一问即可.

下面求解比较复杂的第二问.

设fs[i],gs[i],hs[i]分别表示当满足f,g,h的条件的方案数.

我们发现,fs[i],hs[i]的方案数非常好求,重点,也是gs[i].

我们根据上面的分类讨论可以得知:

对于第一种情况w<0,我们直接求其对应方案数,因为保证了一定有一个f[i]<g[i].

对于第二种情况w=0,我们当f[j]=g[j]时,乘上fs[j]+gs[j],否则乘上gs[j],最后减去所有都选g[j],便保证了每一种方案一定选了一个f[i].

对于第三种情况w>0,我们同样的,只有当f[j]−g[j]=w时,才选一个fs[j],其它都必须选gs[j],因为其它的g[j]都比f[j]优.

这样子,就既能保证选至少一个f[j],且其它都按照最优去选.

最后,我们需要注意,如果题目数据成一条链,这时候,需要的变量可能会非常多,因为这个栈总共有n层.

所以,我们开尽量少的变量,多个寻址,用全局变量储存起来,少开局部变量,控制在5个以内,且不能开longlong,因为这个占字节很多.

除此之外,此题的细节也非常多,需要at least两个小时的细心观察.

code

#include <iostream>
#include <cstdio>
#include <cstring>

#define ll long long

const ll Maxn = 100100, mo = 1e9 + 7;

using namespace std;

ll n,i;

ll a[Maxn],b[Maxn];

ll g[Maxn],f[Maxn],h[Maxn],sg[Maxn],sf[Maxn],sh[Maxn],st[Maxn],cnt,ww,x,t;

ll tov[Maxn*2],next[Maxn*2],last[Maxn],tot;

bool bz[Maxn];

void insert(ll x,ll y)
{
tov[++tot] = y, next[tot] = last[x], last[x] = tot;
}

ll ksm(ll x,ll y)
{
ll sum = 1;
while (y)
{
if (y & 1) sum = (sum * x) % mo;
x = (x * x) % mo;
y >>= 1;
}
return sum;
}

void dfs(int k)
{
int xx = 0, flag = -1, sta = 0, ss = 1;

for (int x = last[k]; x; x = next[x] )
{
if (st[tov[x]]==0)
{
st[tov[x]] = ++cnt;

dfs(tov[x]);

t = min(f[tov[x]],g[tov[x]]), t = min(t,h[tov[x]]), f[k] = f[k] + t;
ww = sf[tov[x]] * (f[tov[x]]==t) + sg[tov[x]] * (g[tov[x]]==t) + sh[tov[x]] * (h[tov[x]]==t);
sf[k] = (sf[k] * ww) % mo;

h[k] = h[k] + g[tov[x]], sh[k] = (sh[k] * sg[tov[x]]) % mo;

if (f[tov[x]]<g[tov[x]]) flag = tov[x];
if (f[tov[x]]-g[tov[x]]<f[sta]-g[sta]) sta = tov[x];
xx += min(f[tov[x]],g[tov[x]]);

ss = (ss * sg[tov[x]]) % mo;
}
}

if ((next[last[k]]>0) || (k==1))
{
if (flag!=-1) g[k] = f[flag] + xx - min(f[flag],g[flag]); else g[k] = f[sta] + xx - g[sta];
ww = 0;
for (x = last[k]; x; x = next[x])
{
if (st[tov[x]]<st[k]) continue;

if (flag!=-1)
{
if (f[tov[x]]<g[tov[x]]) sg[k] = (sg[k] * sf[tov[x]]) % mo; else
if (g[tov[x]]<f[tov[x]]) sg[k] = (sg[k] * sg[tov[x]]) % mo; else
sg[k] = (sg[k] * (sf[tov[x]] + sg[tov[x]])) % mo;
}
if (flag==-1)
{
if (f[sta]-g[sta]==0)
{
if (f[tov[x]]==g[tov[x]]) sg[k] = (sg[k] * (sg[tov[x]] + sf[tov[x]])) % mo;
else
sg[k] = sg[k] * (sg[tov[x]]) % mo;
}
if ((f[sta]-g[sta]>0) && (f[tov[x]]-g[tov[x]]==f[sta]-g[sta])) ww = (ww + (((ss * (ksm(sg[tov[x]],mo-2)) % mo) * sf[tov[x]]) % mo)) % mo;
}
}

if ((flag==-1) && (f[sta]-g[sta]==0)) sg[k] = (sg[k] - ss + mo) % mo;
if ((flag==-1) && (f[sta]-g[sta]>0)) sg[k] = ww;
}
else g[k] = 1e8, sg[k] = 0;
}

int main()
{
scanf("%lld",&n), f[0] = 1e9;
for (ll i = 1; i < n; i++)
scanf("%lld%lld",&a[i],&b[i]), insert(a[i],b[i]), insert(b[i],a[i]), f[i] = sg[i] = sf[i] = sh[i] = 1;
f
= sg
= sf
= sh
= st[1] = cnt = 1;

dfs(1);

printf("%lld\n",min(g[1],f[1]));
if (g[1]<f[1]) printf("%lld",sg[1]); else
if (f[1]<g[1]) printf("%lld",sf[1]); else
printf("%lld",(sg[1]+sf[1]) % mo);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: