题目链接:
Deque and Balls
题目描述:
给你n个数,从第一个数到第n个数按顺序等概率的放入双端队列中,就是说从双端队列的左边放入还是从右边放入是等概率的。 求 个数的期望值 *
mod (1e9 + 7).
题意有点晦涩,举个例子:
n = 3 , 这三个数为:5 2 1
那么可能的情况有:
5 2 1 放入双端队列后是这个序列的概率为 1 / 4 , 满足 的个数为 2 个 (即:5 2 、 2 1);
2 5 1 放入双端队列后是这个序列的概率为 1 / 4 , 满足 的个数为 1 个 (即:5 1);
1 2 5 放入双端队列后是这个序列的概率为 1 / 4 , 满足 的个数为 0 个 ;
1 5 2 放入双端队列后是这个序列的概率为 1 / 4 , 满足 的个数为 1 个 (即:5 2 );
所以最后的期望为 :
答案为:
题解:
首先考虑一个简单的问题,先考虑一个唯一的序列,按题目要求构造一个序列的概率是 (先不管一个序列中会有多少对
)当然该序列中有多少对
就用
乘以该序列中
的对数就是该序列“贡献“的期望值。把
种序列的贡献是加起来再乘以
其实就是: 让你求所有可能的序列中
的对数求和然后再成 2 就是答案(因为
与
化简了)。这样一来问题就会简化了许多。
其次定义一个dp[i]数组,该dp数组的含义为:当第 i 个元素插入队列后有总过有多少对。
再次,就要考虑怎么求 dp[i] 了,显然dp[n](当 i == n 时)就是咱们想要的答案(dp[n]的含义:当全部元素插入队列后有总共有多少对),那么怎么递推 求解dp[i]呢? 首先考虑 dp[ i - 1 ] ,如果咱们知道了 dp[ i - 1 ]有多少对,那么当第 i 个元素加进来的时候,可能在当前序列的左边加进来,也可能在当前序列的右边加进来,所以 首先做的应该是;dp[ i - 1 ] * 2 这个就是前 i - 1 个序列做的贡献,那么第 i 个元素做的贡献是多少呢?
最后,当第i个数字进来的时候,那么前i个数字只要是和第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 个元素 可以有 种排列,对于每一种排列你都可以 找到左边或者右边把 第i个元素放进去使得
个数增加 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);
}
}
}

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

被折叠的 条评论
为什么被折叠?



