JZOJ3482. 【NOIP2013模拟10.23】轮舞前夕 (2017.10B组)

https://jzoj.net/senior/#main/show/3482
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
想法:
一道很经典的树形DP题
f[i]表示第i个点选择.

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

h[i]表示第i个点及其所有儿子都不选,但父亲选的.
最小放置摄像头数
上面三个状态都必须保证以i为根的子树得合法.

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

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

g[i]=min{f[k]+∑j∈son{i}且j≠k min{f[j],g[j]}}
把tot=∑j∈son{i}min{f[j],g[j]}}先算出来,然后取tot-min{f[j],g[j]}+f[j]的最小值

第一问即可.

第二问.

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

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

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

对于第一种情况w<0,我们直接求其对应方案数,因为保证了一定有一个f[i]《g[i].
(如果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],且其它都按照最优去选.

直接dfs会炸,但有90分
特判一条链
最少:n/3
如果n%3>0 n/3+1
方案数:
n%3==0 1
n%3==2 n/3+2
n%3==1 1+4+8+13+19+……(这个数列长n/3+1位)
最后,我们需要注意,如果题目数据成一条链,这时候,需要的变量可能会非常多,因为这个栈总共有n层.

code

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <iostream>
#define ll long long
using namespace std;
const ll maxN=100010,maxn=1e9+7;
ll f[maxN][3],g[maxN][3],tot,tov[maxN*2],next[maxN*2],last[maxN],n,i,x1,y2,ans1,ans2,h[maxN],len[maxN],d[maxN],x,y,head,tail,bz1[maxN],a[maxN],z1,z2,j,z3,s;
bool bz[maxN];
ll min(ll x,ll y)
{
    if (x<y) return x;
    return y;
}
void insert(ll x,ll y)
{
    tov[++tot]=y;
    next[tot]=last[x];
    last[x]=tot;    
} 
void dg(ll x)
{
    ll y=0,i,z=0,z1=0,z2=0,j=0,z3=0,sum=1,c[1000],d[1000],e[1000];
    bool bz1=false;
    i=last[x];
    f[x][2]=1;
    bz[x]=false;
    g[x][1]=1;
    g[x][0]=1;
    g[x][2]=1;
    z=0;
    z3=10000000;
    sum=1;
    c[0]=0;
    d[0]=1;
    while (i>0)
    {
        y=tov[i];
        if (bz[y])
        {
            dg(y);
            f[x][0]+=f[y][1];
            g[x][0]=(g[x][0]*g[y][1])%maxn;
            z1=min(min(f[y][1],f[y][2]),f[y][0]);
            z2=0;
            f[x][2]+=z1;
            for (j=0;j<=2;j++)
                if (f[y][j]==z1) z2=(z2+g[y][j])%maxn;
            g[x][2]=(g[x][2]*z2)%maxn;;
            z+=min(f[y][1],f[y][2]);
            z3=min(f[y][2]-f[y][1],z3);
            bz1=true;
            bz[y]=true;
            c[++c[0]]=y;
            d[c[0]]=d[c[0]-1]*g[y][1];
            h[y]=c[0];
            sum=(sum*g[y][1])%maxn;
        }
        i=next[i];
    }
    e[c[0]+1]=1;
    for (i=c[0];i>0;i--)
        e[i]=e[i+1]*g[c[i]][1];
    f[x][1]=1000000000;
    i=last[x];
    if (z3>0) g[x][1]=0;
    while (i>0)
    {
        y=tov[i];
        if (bz[y])
        {
            bz[y]=false;
            f[x][1]=min(f[x][1],z-min(f[y][1],f[y][2])+f[y][2]);
            if (z3<0) 
            {
                z2=0;
                z1=min(f[y][1],f[y][2]);
                for (j=1;j<=2;j++)
                {
                    if (f[y][j]==z1) z2=(z2+g[y][j])%maxn;
                }
                g[x][1]=(g[x][1]*z2)%maxn;
            }
            if (z3==0) 
            {
                if (f[y][1]==f[y][2]) g[x][1]=(g[x][1]*(g[y][1]+g[y][2])%maxn)%maxn;
                else g[x][1]=(g[x][1]*g[y][1])%maxn;
            }
            if (z3>0)
            {
                if (z-min(f[y][1],f[y][2])+f[y][2]==f[x][1])
                {
                    g[x][1]=(g[x][1]+(d[h[y]-1]*e[h[y]+1])%maxn*g[y][2]%maxn)%maxn;
                }
            }
        }
        i=next[i];
    }
if (z3==0) g[x][1]=(g[x][1]-sum+maxn)%maxn;
} 
int main()
{
    //freopen("a.in","r",stdin);
    scanf("%lld",&n);
    for (i=1;i<=n-1;i++)
    {
        scanf("%lld%lld",&x1,&y2);
        bz1[x1]++;
        bz1[y2]++;
        insert(x1,y2);
        insert(y2,x1);
    }
    for (i=1;i<=n;i++)
        if (bz1[i]==1)
        {
            x=i;
            break;
        }
    memset(bz,true,sizeof(bz));
    head=0;
    tail=1;
    d[1]=x;
    len[x]=1;
    bz[x]=false;
    a[1]=x;
    while (head<tail)
    {
        head++;
        x=d[head];
        i=last[x];
        while (i>0)
        {
            y=tov[i];
            if (bz[y])
            {
                bz[y]=false;
                len[y]=len[x]+1;
                a[len[y]]=y;
                d[++tail]=y;
            }
            i=next[i];
        }
    }
    tot=0;
    for (i=1;i<=n;i++)
        if (len[i]>tot) tot=len[i];
    if (tot==n)
    {
        x=a[tot];
        f[x][0]=0;
        f[x][2]=1;
        f[x][1]=10000000;
        g[x][0]=1;
        g[x][2]=1;
        g[x][1]=1;
        for (i=tot-1;i>0;i--)
        {
            x=a[i];
            y=a[i+1];
            f[x][0]=0;
            f[x][2]=1;
            f[x][1]=10000000;
            g[x][0]=1;
            g[x][2]=1;
            g[x][1]=1;
            f[x][0]+=f[y][1];
            g[x][0]=(g[x][0]*g[y][1])%maxn;
            z1=min(min(f[y][1],f[y][2]),f[y][0]);
            z2=0;
            f[x][2]+=z1;
            for (j=0;j<=2;j++)
                if (f[y][j]==z1) z2=(z2+g[y][j])%maxn;
            z3=f[y][1]-f[y][2];
            g[x][2]=(g[x][2]*z2)%maxn;
            f[x][1]=f[y][2];
            if (z3>=0) g[x][1]=(g[x][1]*g[y][2])%maxn;
            if (z3<0)
            {
                z2=0;
                z1=min(f[y][1],f[y][2]);
                for (j=1;j<=2;j++)
                {
                    if (f[y][j]==z1) z2=(z2+g[y][j])%maxn;
                }
                g[x][1]=(g[x][1]*z2)%maxn;
             } 
        }
        x=a[1];
        ans1=min(f[x][1],f[x][2]);
        printf("%lld\n",ans1);
        if (n%3==0) ans2=1;
        if (n%3==2) ans2=n/3+2;
        if (n%3==1) 
        {
            s=1;
            x=3;
            ans2=1;
            for (i=1;i<=n/3;i++)
            {
                s=(s+x)%maxn;
                ans2=(ans2+x)%maxn;
                x=(x+1)%maxn;
            }
            printf("%lld",ans2);
         } 
        return 0;   
    }
    memset(bz,true,sizeof(bz));
    dg(1);
    ans1=min(f[1][1],f[1][2]);
    printf("%lld\n",ans1);
    ans2=0;
    if (ans1==f[1][1]) ans2+=g[1][1];
    if (ans1==f[1][2]) ans2+=g[1][2];
    printf("%lld",ans2);  
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值