【loli的胡策】联校10.26(抖动dp+树形dp*期望)

19 篇文章 0 订阅
博客介绍了如何利用动态规划和期望计算解决一道关于蚊子消灭的问题,其中涉及到了树形DP在解决蚊子在兔子洞中路径问题的应用,以及如何计算每个蚊子生存的概率。给出了样例输入输出和数据范围,并讨论了O(m^2 log n)的暴力解法以及优化后的O(n)解决方案。
摘要由CSDN通过智能技术生成

T1

[BZOJ1925][Sdoi2010]地精部落

题解

啊原题。。。。。。
考场上打表70pts?
以为是什么ksm+组合乱搞,谁知道T1就来个 noip难度的 dp。。。
代码一分钟,题解两小时。

T3:

【题目描述】

因为蚊子人多势众,所以它们分兵m*(m-1)路.m是整个兔子洞中只和一条通道相邻的洞穴数目.任意两个这样的洞穴a,b之间(也就是任意两个叶子节点之间)会有两只蚊子,一只从a飞到b,一只从b飞到a.它们都沿着a到b的最短路径移动.蚊子每秒钟可以通过一条通道.所有蚊子都在0s时突然出现在起点并开始移动.每只蚊子在到达终点后的一瞬间都会突然消失.有些蚊子并不会经过兔子所在的1号节点,它们起到的是恐吓作用.

兔子有一个灭蚊器,这个灭蚊器被放在1号节点.每个时刻,它都会工作一次,把和灭蚊器距离小于等于d范围内的蚊子全部杀死.(d=0时只能控制1号点一个位置)

遗憾的是,每个时刻灭蚊器只有p/q的概率能够正常工作.如果不能正常工作,那么蚊子将不受到任何影响.

请算出灭蚊器在一晚上期望能杀死多少蚊子
因为兔子讨厌小数,你需要输出这个期望值模109+7后的结果.即:如果期望值可以表示成有理数a/b(a,b为整数),你需要输出 ab1 mod 109+7 的值.

【输入格式】
第一行一个整数n,表示兔子洞中洞穴的个数.洞穴编号为1到n的整数.
接下来n-1行,每行两个整数u,v,表示u和v两个洞穴之间有一条通道.
接下来一行三个整数d,p,q,表示灭蚊器的作用范围是d,每个时刻工作的概率是p/q.
【输出格式】
一行一个整数ans,表示期望模 109+7 的值.

【样例输入】
3
1 2
1 3
1 1 2
【样例输出】
750000007

【样例解释】
共有2只蚊子,一只从2飞到3,一只从3飞到2.灭蚊器的作用范围是1,那么三个点都在作用范围内,每个蚊子会有三个时刻在作用范围内,那么每只蚊子生还的概率都是1/8,经过计算,我们期望能够打死7/4只蚊子,.我们输出 741 mod 109+7 的值750000007.
【数据范围】
记m为叶子节点的个数. 对于第1个测试点,n=300 对于第2,3个测试点,n=3000
对于第4个测试点,d=0,n=100000 对于第5个测试点,p/q=1,n=100000
对于第6,7个测试点n=5000000,m<=500 对于第8,9,10个测试点,n=5000000
对于所有测试点,m < n <=5000000,0<=d<=n,1<=p<=q<= 109+7 保证1号节点至少和两条通道相连.

题解:

考试时暴力 O(m2logn) 起码有50pts,然后就爆零了???虽然思路是对的但不知哪里写挂了

100pts:

每只蚊子的贡献是1-(1-p/q)^t(t是暴露时间)
我们可以先不管前面那个1,把后面的 (1p/q)t 求出来,最后用m*(m-1)减去那个和就可以了

一条路径可以在LCA处拆成两条,一部分是从一个叶节点走到LCA,另一部分是从LCA走到另一个叶节点,把这两部分看作两条“半路径”
我们考虑如何求出以某个点为LCA的所有路径的贡献之和

记g[i]为以i为LCA的所有半路径的贡献之和
对于i子树内的每个叶节点xg[i]+=(1-p/q)^k,k是x到i的半路径上的被控制点的个数
g[i]可以通过O(n)树形dp得到

接下来我们通过g[i]求出以每个点为LCA的路径的贡献之和
对于点x,考虑它的所有儿子,每一对儿子(u,v)的贡献是g[u] * g[v] * (1-p/q)
注意,(u,v)和(v,u)都需要算一次
这里写图片描述
直接暴力枚举每一对儿子会超时
我们通过乘法分配律可以用O(儿子个数)的时间复杂度算出来
总的时间复杂度仍为O(n)
lemon还需要开栈。。。

代码:

#include <cstdio>
#define LL long long 
#define N 5000005
using namespace std;
const int mod=1e9+7;
LL p,q,ans,g[N],gl;int d,tot,nxt[N*2],point[N],v[N*2],n,m;
LL ksm(LL a,int k)
{
    LL ans=1;
    for (;k;k>>=1,a=a*a%mod)
      if (k&1) ans=ans*a%mod;
    return ans;
}
void addline(int x,int y)
{
    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
    ++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
void dfs(int x,int fa,int dep)
{
    int size=0;LL wo=0;
    for (int i=point[x];i;i=nxt[i])
      if (v[i]!=fa)
      {
        size++;
        dfs(v[i],x,dep+1);
        wo=(wo+g[x]*g[v[i]]%mod)%mod;
        g[x]=(g[x]+g[v[i]])%mod;
      }
    wo=wo*2%mod;
    if (!size) g[x]=1,m++;
    if (dep<=d) g[x]=g[x]*gl%mod,wo=wo*gl%mod;
    ans=(ans+wo)%mod;
}
int main()
{
    freopen("mosquito.in","r",stdin);
    freopen("mosquito.out","w",stdout);
    scanf("%d",&n);int i,j;
    for (i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        addline(x,y);
    }
    scanf("%d%d%d",&d,&p,&q);
    gl=(1-p*ksm(q,mod-2)%mod+mod)%mod;
    dfs(1,0,0);
    ans=((LL)m*(m-1)-ans+mod)%mod;
    printf("%lld",ans);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值