ZOJ-3929-Deque and Balls【思维】【好题】

3929-Deque and Balls

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 (x1, x2, …, xn) be the labels of the balls in the deque from left to right. The beauty of the deque B(x1, x2, …, xn) is defined as the number of descents in the sequence. For the sequence (x1, x2, …, xn), a descent is a position i (1 ≤ i < n) with xi > xi+1.

You need to find the expected value of B(x1, x2, …, 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: p1, p2, …, 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

题目链接:ZOJ-3929

题目大意:每次等概率地往一个序列的左边或右边放一个球,问期望的相邻逆序对有多少个

思考点出发:当放入第i个球,恰好在第j个球左边/右边的概率是多少?

稍微找一下规律:第i号球

• 和第i-1号球相邻的概率是1/2
• 和第i-2号球相邻的概率是1/4
...

以此类推,一个例外是与1号球相邻的概率和与2号球相邻的概率是相同的

做法思路:维护一个数据结构,用于查询比x小的元素的概率之和是多少,再维护一个数据结构查询比x大的元素的概率之和是多少,然后把查询的结果加起来

既然如此,那就可以直接用一个数组,查询x时,计算不等于x的概率就可以了

最后答案会乘以 2 ^ n,因此所有的概率都能用整数表示

以下是代码:

//
// ZOJ-3929-Deque and Balls.cpp
// ZOJ
//
// Created by pro on 16/4/11.
// Copyright (c) 2016年 pro. All rights reserved.
//

/* 解题思路: 比赛时的想法: 1.dp[i][0]表示,第i个数加在左边的情况,dp[i][1]表示,第i个数加在右边的情况。 2.以左边为例: a)首先dp[i][0] = dp[i - 1][0] + dp[i - 1][1]; //把前面的值加起来 b)然后处理第i个数的情况,第i个数加在左边和其旁边数的关系。我们可以知道,在i之前的所有数字都可能出现在i左边。 并且发现一个规律,第一个数字出现1次,第二个数字出现1次,第三个数字出现2次,第四个数字出现4次。。。 c)所以利用树状数组求出第i个数字之前,且比i小的数字,a[pos] = pos^(i - 1)的和。存在cnt1数组中加在dp上即可 d)右边同理。 这种思路,比赛是是过了,但是刚刚发现同样的代码超时了。于是进行了优化 1.首先,上述cnt1是记录在i这个数字之前且比i小的数字*他的位置的平方。cnt2(处理右边情况)是记录在i这个数字之前且比i大的数字的情况。 2.也就是说,合并一下,就是处理在i之前和i值不同的即可。 3.所以,做法优化为,求出所有情况,减去相同情况 */
#include <stdio.h>
#include <iostream>
#include <cstring>
#include <string>
#include <map>
using namespace std;
#define MAXN 100005
#define ll long long
#define mod 1000000007
ll dp[MAXN];
ll same[MAXN];
ll cnt[MAXN];
map <ll,int> Same;
void solve()
{
    cnt[1] = 1;
    for (int i = 2; i < MAXN; i++)
    {
        cnt[i] = (cnt[i - 1] * 2) % mod;
    }
}
int main()
{
    int t;
    cin >> t;
    while(t--)
    {
        int n;
        scanf("%d",&n);
        Same.clear();
        memset(dp,0,sizeof(dp));
        solve();
        for (int i = 1; i <= n; i++)
        {
            int a;
            scanf("%d",&a);
            dp[i] = ((dp[i - 1] * 2) % mod + cnt[i - 1] - Same[a] + mod) % mod;
            if (i == 1)
            {
                Same[a] = (Same[a] + 1) % mod;
            }
            else
            {
                Same[a] = (Same[a] + cnt[i - 1]) % mod;
            }
        }
        printf("%lld\n",(dp[n] * 2) % mod);
    }

    return 0;
}

你可能感兴趣的:(ZOJ,3929)