【ZOJ3929 The 16th Zhejiang University Programming ContestC】【DP】Deque and Balls n个数放入双端队列2^n种方案有多少个位置

这是一道关于计算双端队列中球的排列期望值的问题。题目给出n个标签为pi的球,每次将球随机地放入双端队列的两端。定义序列中降序的位置为美丽度,需要求解所有可能排列的美丽度的期望值。题目要求输出期望值乘以2^n对1e9 + 7取模的结果。输入包含多组测试用例,每组用例包含球的数量n和每个球的标签。
摘要由CSDN通过智能技术生成

Deque and Balls

Time Limit: 2 Seconds        Memory Limit: 65536 KB

There are n balls, where the i-th ball is labeled as pi. You are going to put n balls into a deque. In the i-th turn, you need to put the i-th ball to the deque. Each ball will be put to both ends of the deque with equal probability.

Let the sequence (x1x2, ..., xn) be the labels of the balls in the deque from left to right. The beauty of the deque B(x1x2, ..., xn) is defined as the number of descents in the sequence. For the sequence (x1x2, ..., xn), a descent is a position i (1 ≤ i < n) with xi > xi+1.

You need to find the expected value of B(x1x2, ..., xn).

Deque is a double-ended queue for which elements can be added to or removed from either the front (head) or the back (tail).

Input

There are multiple test cases. The first line of input contains an integer T, indicating the number of test cases. For each test case:

The first line contains an integer n (2 ≤ n ≤ 100000) -- the number of balls. The second line contains n integers: p1p2, ..., pn (1 ≤ pi ≤ n).

Output

For each test case, if the expected value is E, you should output E⋅2n mod (109 + 7).

Sample Input
2
2
1 2
3
2 2 2
Sample Output
2
0

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }
const int N = 1e5+10, M = 0, Z = 1e9 + 7, ms63 = 0x3f3f3f3f;
int casenum, casei;
int n, x;
int b[N];
int lft[N], rgt[N];
void add(int &x, int y)
{
	x += y;
	if (x >= Z)x -= Z;
}
void modify(int a[], int x, int val)
{
	for (; x <= n; x += x&-x)add(a[x], val);
}
int check(int a[], int x)
{
	int ret = 0;
	for (; x; x -= x&-x)add(ret, a[x]);
	return ret;
}
int main()
{
	b[0] = 1;for (int i = 1; i <= 1e5; ++i)b[i] = b[i - 1] * 2 % Z;
	scanf("%d", &casenum);
	for (casei = 1; casei <= casenum; ++casei)
	{
		scanf("%d", &n);
		for (int i = 1; i <= n; ++i)lft[i] = rgt[i] = 0;
		scanf("%d", &x);
		modify(lft, x, 2);
		modify(rgt, x, 2);
		int ans = 0;
		for (int i = 2; i <= n; ++i)
		{
			scanf("%d", &x);
			ans = ans * 2 % Z;
			add(ans, check(lft, x - 1));
			add(ans, (Z + check(rgt, n) - check(rgt, x)) % Z);
			modify(lft, x, b[i-1]);
			modify(rgt, x, b[i-1]);
		}
		printf("%d\n", ans);
	}
	return 0;
}
/*
【题意】
有n(1e5)个数。
每个数都有一个权值(1~n)
我们有一个双端队列,每个数加入到双端队列的时候,可以加在该队列的前部或者尾部。
这样我们最后一共可以生成2^n个队列。

我们对于这2^n个队列,都扫描一遍。
如果存在一个队列的一个位置i∈[1,n),满足a[i]>a[i+1],那么我们答案的贡献就+1
问你最后的答案是多少(mod (1e9+7))

【类型】
DP 树状数组

【分析】
这题我们可以DP来做。如何做呢?
我们用lft[i]表示数值为i的数出现在整个数列左侧的方案数
   用rgt[i]表示数值为i的数出现在整个数列右侧的方案数

那么,当新的一个数加进来时——
1,之前的所有答案都要*2,因为队列的可能性种数*2了,所以之前的答案也要*2
2,对于之前的每个lft[i],它依然继续是lft的条件是当前的数放在了右侧,
	这样的话,对于当步决策,使得之前lft[]变化的延展系数为1,所以lft[i]保留不变
3,对于之前的每个rgt[i],它依然继续是rgt的条件是当前的数放在了左侧,
	这样的话,对于当步决策,使得之前rgt[]变化的延展系数为1,所以rgt[i]保留不变

于是,在ans*=2后,当步数新放置后,对答案的贡献是lft[1~x-1]+rgt[x+1~n]
这样我们就可以AC啦。

【时间复杂度&&优化】
O()

【数据】
input
3
1 2 3
output

detail
1 2
2 1
1 2 3
2 1 3
3 1 2
3 2 1


*/





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值