JZOJ3482. 【NOIP2013模拟10.23】轮舞前夕 (2017.10B组)
来源:互联网 发布:淘宝财务运营分析 编辑:程序博客网 时间:2024/05/20 16:43
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 longusing 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); }
- JZOJ3482. 【NOIP2013模拟10.23】轮舞前夕 (2017.10B组)
- NOIP2013模拟10.23轮舞前夕
- [jzoj]3482. 【NOIP2013模拟10.23】轮舞前夕(经典树形DP)
- JZOJ3481. 【NOIP2013模拟10.23】君と彼女の恋(2017.10B组)
- JZOJ3425. 【NOIP2013模拟】能量获取(2017.8B组)
- JZOJ3426. 【NOIP2013模拟】封印一击 (2017.8B组)
- JZOJ3427. 【NOIP2013模拟】归途与征程 (2017.8B组)
- [jzoj]3503. 【NOIP2013模拟11.4B组】粉刷(paint)(位运算优化)
- jzoj[3521]. 【NOIP2013模拟11.7B组】道路覆盖(cover) (状压DP)
- JZOJ3456. 【NOIP2013模拟联考3】恭介的法则(rule)(2017.8B组)
- JZOJ3457. 【NOIP2013模拟联考3】沙耶的玩偶(doll) (2017.8B组)
- 3494. 【NOIP2013模拟联考13】线段(segment) (2017.9B组)
- 高中OJ3502. 【NOIP2013模拟11.4B组】方格游戏(game)
- 高中OJ3503. 【NOIP2013模拟11.4B组】粉刷(paint)
- 高中OJ3504. 【NOIP2013模拟11.4B组】运算符(calc)
- JZOJ-senior-3502. 【NOIP2013模拟11.4B组】方格游戏
- 高中OJ3514. 【NOIP2013模拟11.6B组】最小比例
- 高中OJ3515. 【NOIP2013模拟11.6B组】软件公司
- 经纬度10进制快速转换
- RTKLIB:RTK使用介绍
- Java面试题集(1)
- 坚持#第205天~培训前尽力学习云计算,终于100套那个弄完了,我是有梦想的男人
- 最大连续子序列的四种求解方法
- JZOJ3482. 【NOIP2013模拟10.23】轮舞前夕 (2017.10B组)
- Spell checker (poj 1035 字符串)
- DXP 模块化布局
- 删除单链表中绝对值重复的元素
- Portlet 通信(三) Public render parameters(共享渲染/呈现参数)
- VMware10中的Linux系统利用NAT网络连接方式访问外网配置
- 并发编程 C++11 互斥量使用
- 第四章语句十个问题
- 代码摘录