ZOJ-3929 Deque and Balls (DP+找规律)

题目大意:n个数,每个数的大小都在1~n之间。操作n次,第 i 次将第 i 个数放到一个双端队列里面,放到队列两端的概率是相等的。问操作n次之后双端队列中元素满足xi>xi+1的对数的期望,输出的数据为:(期望*2^n)%mod。

题目分析:定义状态dp(i)表示操作 i 次之后的相应期望值。则状态转移方程为:

dp(i)=1/2*(dp(i-1)+k1)+1/2*(dp(i-1)+k2)  (两种情况,放在队首和队尾) 其中,k1表示比a(i)小的元素可能与a(i)相邻的总次数,k2表示比a(i)大的元素可能与a(i)相邻的总次数。

合并一下得到:dp(i)=dp(i-1)+(k1+k2)/2 ,因为输出的数据为 “(期望*2^n)%mod”,所以,改变dp(i)的含义为如果操作 i 次要输出的结果,则:

dp(i)=2*dp(i-1)+k1+k2=2*dp(i-1)+k(i-1)-num(a(i)),其中,k(i-1)表示前i-1个数与第 i 个数相邻的可能总次数,num(a(i))表示到目前为止与a(i)相等的元素可能与a(i)相邻的总次数。

有个规律能够快速的求出k(i)和num(a(i))。假如当前在进行第 i 次操作,那么第1、第2、第3...元素可能与a(i)相邻的次数为1、1、2、4、8...

只需将上述规律要出表f,就能快速查找k(i)和num(a(i))以及更新num(a(i))。

 

代码如下:

# include<iostream>
# include<cstdio>
# include<cstring>
# include<vector>
# include<queue>
# include<list>
# include<set>
# include<map>
# include<string>
# include<cmath>
# include<cstdlib>
# include<algorithm>
using namespace std;
# define LL long long

const int N=1005;
const int INF=1000000000;
const LL oo=0x7fffffffffffffff;
const double eps=1e-10;
const int mod=1000000007;

int n;
LL sum[N*100];
LL f[N*100];
LL dp[N*100];
LL num[N*100];

void init()
{
	f[0]=f[1]=1;
	for(int i=2;i<=100000;++i)
		f[i]=(2*f[i-1])%mod;
	sum[0]=1;
	for(int i=1;i<=100000;++i)
		sum[i]=(f[i]+sum[i-1])%mod;
}

int main()
{
	init();
	int T;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		memset(num,0,sizeof(num));
		memset(dp,0,sizeof(dp));
		LL a;
		scanf("%lld",&a);
		dp[1]=0;
		num[a]=f[0]%mod;
		for(int i=2;i<=n;++i){
			scanf("%lld",&a);
			dp[i]=(2*dp[i-1]+sum[i-2]-num[a]+mod)%mod;
			num[a]=(num[a]+f[i-1])%mod;
		}
		printf("%lld\n",(dp[n]*2)%mod);
	}
	return 0;
}

  

posted @ 2016-04-30 22:31  20143605  阅读(301)  评论(0编辑  收藏  举报