標籤:
題目大意: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 longconst 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;}
ZOJ-3929 Deque and Balls (DP+找規律)