题意:链接
方法:树形DP+贪心
解析:这是一道好题。
好首先要明确这题求的是什么?
名义上是期望值,而实际上就是找一条路径。什么路径呢?从根节点走遍所有的叶子节点所花费步数最短的路径。
明确了题意后该怎么做呢?
首先看我们需要什么?
目前有个根节点,我们需要知道从他向一个分支走,失败步数是多少,成功步数是多少?
那么怎么维护我们需要的东西呢?
首先我们先给他们起个名:suc,fai;
其次再给一个节点的叶子节点的个数起个名:son
起名完事之后我们就要更新了。
先谈叶子节点,显然叶子节点的suc[x]=0,fai[x]=0,son[x]=1;
之后就是向上更新了,son和fai也很好搞
对于son的更新son[fa]+=son[x];
对于fai的更新fai[fa]+=fai[x]+2(此时worm[x]=0);否则的话不用管。
对于最不好弄的suc更新:
首先对于我们当前讨论的要走的子节点pn,这时候,蜗牛已经经过了走p1~pn-1的失败的步数,所以这些失败的步数是要记录的,我们给它命名为cnt-fai,此时我们会多走几个cnt-fai呢?我们发现,一共会多走son[pn]个cnt-fai,然而每次走过一个叶子节点后,更新一次suc[x],然后再将其视为失败,返回,找下一个叶子节点,此时我们就会发现,再返回到px的时候,这个蜗牛还需要继续返回一层,即返回到目前的根节点x,多走出一步,多走多少个一步呢?仍然是son[pn]个,用公式来写就是这样:suc[x]+=(cntfai+1)?son[pn]+suc[pn]
同时更新cntfai,cntfai+=fai[pn]+2这个2就是指去以及回来的两步
以上,dp的部分差不多搞定了,观察上面的式子发现,唯一不能确定的就是pn是什么鬼?
也就是说,对于一个根节点x我们按照什么顺序来讨论他的子节点会使得x的suc最小呢?
以下引用discuss里某神犇的证明
假设交换相邻的两颗子树的选择顺序,设P1,P2为选他们的概率,A1,A2为房子确实在上面所需的步数,B1,B2为实际上不在上面所需的步数,则
调整后:Delta=P1*A1+P2*(B1+A2)-P2*A2-P1*(B2+A1)=P2*B1-P1*B2
于是Delta<0 <=> B1/P1小于B2/P2
而题设情况即为Delta<0
于是应按照B/L排序 即遍历此子树所需步数/其所含叶子树
转化为公式呢?就是这个东西
(fai[u]+2)?son[v]<(fai[v]+2)?son[u]
其中u和v分别是两个子节点。
后记:这题是道好题,做完后能学到不少东西,值得一做,然而自己在做的时候对于这种双线的把握还不是很好,比较欠缺,什么鬼的排序根本没想到,自己对于这种难题的把握还是差很多啊!
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 1100
using namespace std ;
int n,cnt;
bool col[N];
int head[N],suc[N],fai[N],son[N];
char s[5];
struct node
{
int to,next;
}edge[N];
void init()
{
memset(head,-1,sizeof(head));
memset(col,0,sizeof(col));
memset(suc,0,sizeof(suc));
memset(fai,0,sizeof(fai));
memset(son,0,sizeof(son));
cnt=1;
}
int cmp(int u,int v)
{
return (fai[u]+2)*son[v]<(fai[v]+2)*son[u];
}
void edgeadd(int from,int to)
{
edge[cnt].to=to;
edge[cnt].next=head[from];
head[from]=cnt++;
}
void dfs(int u)
{
if(head[u]==-1)
{
suc[u]=0,son[u]=1,fai[u]=0;
}
int tmp[N],tot=0;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int to=edge[i].to;
tmp[++tot]=to;
dfs(to);
son[u]+=son[to];
if(!col[u])fai[u]+=fai[to]+2;
}
sort(tmp+1,tmp+1+tot,cmp);
int cnt_fai=0;
for(int i=1;i<=tot;i++)
{
suc[u]+=(cnt_fai+1)*son[tmp[i]]+suc[tmp[i]];
cnt_fai+=fai[tmp[i]]+2;
}
}
int main()
{
while(scanf("%d",&n)&&n!=0)
{
init();
for(int i=1;i<=n;i++)
{
int pre;
scanf("%d%s",&pre,s);
col[i]=s[0]==‘Y‘?1:0;
if(pre==-1)continue;
edgeadd(pre,i);
}
dfs(1);
printf("%.4lf\n",(double)suc[1]/(double)son[1]);
}
}
时间: 2024-10-31 13:10:36