Deque and Balls ZOJ - 3929 (组合数学+dp+期望)

本文深入探讨了ZOJ-3929 DequeandBalls问题,通过动态规划方法解决球按等概率放入双端队列的期望值问题,详细解释了dp数组的构建过程与组合数的应用。

题目链接:

Deque and Balls

ZOJ - 3929

题目描述:

给你n个数,从第一个数到第n个数按顺序等概率的放入双端队列中,就是说从双端队列的左边放入还是从右边放入是等概率的。 求 x_{i} > x_{i + 1} 个数的期望值 * 2^n mod (1e9 + 7).

题意有点晦涩,举个例子:

n = 3 , 这三个数为:5 2 1

那么可能的情况有: 
5 2 1   放入双端队列后是这个序列的概率为 1 / 4 , 满足 x_{i} > x_{i + 1} 的个数为 2 个  (即:5 2 、 2 1); 
2 5 1   放入双端队列后是这个序列的概率为 1 / 4 , 满足 x_{i} > x_{i + 1} 的个数为 1 个  (即:5 1);
1 2 5   放入双端队列后是这个序列的概率为 1 / 4 , 满足 x_{i} > x_{i + 1} 的个数为 0 个  ;
1 5 2   放入双端队列后是这个序列的概率为 1 / 4 , 满足 x_{i} > x_{i + 1} 的个数为 1 个  (即:5 2 );

所以最后的期望为 : E = 2 \times \frac{1}{4} + 1 \times \frac{1}{4} + 0 \times \frac{1}{4} + 1 \times \frac{1}{4}

答案为:ans = (2 \times \frac{1}{4} + 1 \times \frac{1}{4} + 0 \times \frac{1}{4} + 1 \times \frac{1}{4}) \times 2^3\ mod (10^9+7) =8

题解:

首先考虑一个简单的问题,先考虑一个唯一的序列,按题目要求构造一个序列的概率是 \frac{1}{2^{n-1}} (先不管一个序列中会有多少对 x_{i} > x_{i + 1} )当然该序列中有多少对x_{i} > x_{i + 1}就用 \frac{1}{2^{n-1}}乘以该序列中x_{i} > x_{i + 1}的对数就是该序列“贡献“的期望值。把2^{n - 1}种序列的贡献是加起来再乘以 2^n 其实就是:  让你求所有可能的序列中x_{i} > x_{i + 1}的对数求和然后再成 2 就是答案(因为 2^n\frac{1}{2^{n-1}} 化简了)。这样一来问题就会简化了许多。

其次定义一个dp[i]数组,该dp数组的含义为:当第 i 个元素插入队列后有总过有多少对x_{i} > x_{i + 1}

再次,就要考虑怎么求 dp[i] 了,显然dp[n](当 i == n 时)就是咱们想要的答案(dp[n]的含义:当全部元素插入队列后有总共有多少对x_{i} > x_{i + 1}),那么怎么递推 求解dp[i]呢? 首先考虑 dp[ i - 1 ] ,如果咱们知道了 dp[ i - 1 ]有多少对,那么当第 i 个元素加进来的时候,可能在当前序列的左边加进来,也可能在当前序列的右边加进来,所以 首先做的应该是;dp[ i - 1 ] * 2 这个就是前 i - 1 个序列做的贡献,那么第 i 个元素做的贡献是多少呢? 

最后,当第i个数字进来的时候,那么前i个数字只要是和第i个数字大小不一样就可以贡献 1 个x_{i} > x_{i + 1}

比如说: 5 2 1 
当 1 进来的时候,可以是: 5 2 1 这时候 2 贡献一次。 2 5 1 这时候 5 贡献一次。

再比如 5 2 1 6

当 6 进来后 ,可以是 : 6 5 2 1 、 6 2 5 1、 6 1 2 5 、6 1 5 2 、一共多出来4次。

所以根据排列组合(你可以看做是规律,其实是组合数),怎么求呢? 其实就是: 假设第 i 个元素,与前 i - 1 个元素都不相同,所以 前 i - 1 个元素 可以有 2^{i - 2} 种排列,对于每一种排列你都可以 找到左边或者右边把 第i个元素放进去使得 x_{i} > x_{i + 1} 个数增加 1 。 这是假设与前 i - 1 个元素都不相同的时候,那么如果相同怎么办呢? 如果相同就 减去 就好啦~~ 每次都记录下,最后如果碰到相同的就减去。(如果还有不明白的请看代码,有详细注释,相信看下代码就都明白啦~!)

最终 dp[ i ] = dp[i] = (2 * dp[i - 1] + sum[i - 1] - re[t]) % mod;

sum记录的是 第i个元素的新增的贡献 
re记录的是相同的,需要减去的个数

AC代码:

#include<bits/stdc++.h>
#define up(i, x, y) for(int i = x; i <= y; i++)
#define down(i, x, y) for(int i = x; i >= y; i--)
#define maxn ((int)1e5 + 10)
using namespace std;
typedef long long ll;
ll f[maxn], arr[maxn], re[maxn], sum[maxn], dp[maxn];
ll mod = 1e9 + 7;
void init() // 先打一个表,以后直接用了
{
    int  n = 100000 + 2;
    f[1] = 1; f[2] = 1;//这里比较特殊,当 n = 1 时 是1,因为就一个元素嘛。
    up(i, 3, n)
    {
        f[i] = f[i - 1] * 2 % mod; //就是求 组合数的,打一个表,按照 2 ^ (i - 2) 这个公式打的表
    }
    up(i, 1, n)
    {
        sum[i] += sum[i - 1] + f[i] % mod; //求一个前缀和,因为 需要累加的嘛~
    }
}
int main()
{
    int T;init();
    while(~scanf("%d", &T))
    {
        while(T--)
        {

            memset(re, 0, sizeof(re));
            memset(dp, 0, sizeof(dp));
            int n; scanf("%d", &n);
            up(i, 1, n)
            {
                int t; scanf("%d", &t);
                dp[i] = (2 * dp[i - 1] + sum[i - 1] - re[t]) % mod;
                re[t] = (re[t] + f[i]) % mod;  // 
            }
            printf("%lld\n", dp[n] * 2LL % mod);
        }
    }
}

 

 

 

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值