1. 程式人生 > >TopCoder SRM 666 Div1 444 SumOverPermutations

TopCoder SRM 666 Div1 444 SumOverPermutations

感覺是道好題~TC好多題就這樣,想半天,然後程式碼幾行就完了。 原來碰到這種排列的dp就一臉懵逼,因為狀態特別難設計,每次都感覺只會狀壓,現在終於有點get到其中的套路了。

在這題裡我們可以發現幾個事實

  • 每個位置對答案的貢獻只和它左右兩邊是否比它早確定有關
  • 我們可以通過將ii插入11i1i-1的排列中得到所有11ii的所有排列

於是我們可以弄一個f[o][i][j]f[o][i][j]表示到第oo個位置,這個位置在前oo個裡面是第ii個確定的,以及前一個位置是否比它早確定,j=0j=0表示早,j=1j=1表示晚。 轉移的話就列舉一下第o+

1o+1個位置是第幾個被確定的。然後這樣複雜度還是O(n3)O(n^3)爆炸,於是就加個字首和優化,再順便滾動陣列一下就過了。

然而,我一開始T了!!!震驚,說好的TC一秒1e9呢(霧),欺騙感情!! 結果發現是我手動取模爆炸了。。。。。。改了就過了。。。。。。

#include <bits/stdc++.h>
#define fr(i,x,y) for(int i=x;i<=y;i++)
#define ll long long
using namespace std;
const int N=4001;
const int p=1e9+7;
ll f[2][N]
[2]; template<class T> void checkmin(T &a,const T &b) { if (b<a) a=b; } template<class T> void checkmax(T &a,const T &b) { if (b>a) a=b; } class SumOverPermutations { public: int findSum( int n ) ; }; void Add(ll &x,ll y){ x+=y; while(x<0) x+=p; while
(x>=p) x-=p; } int SumOverPermutations::findSum(int n) { int cur=1; f[1][1][1]=1; fr(o,2,n){ cur^=1; //memset(f[cur],0,sizeof f[cur]); fr(i,1,o){ f[cur][i][0]=f[cur][i][1]=0; Add(f[cur][i][0],(n-1)*f[cur^1][i-1][0]%p+n*f[cur^1][i-1][1]%p); Add(f[cur][i][1],(n-2)*(f[cur^1][o-1][0]-f[cur^1][i-1][0])%p); Add(f[cur][i][1],(n-1)*(f[cur^1][o-1][1]-f[cur^1][i-1][1])%p); } if (o==n) continue; fr(i,1,o){ Add(f[cur][i][0],f[cur][i-1][0]); Add(f[cur][i][1],f[cur][i-1][1]); } } ll ans=0; fr(i,1,n){ Add(ans,f[cur][i][0]*(n-1)%p); Add(ans,f[cur][i][1]*n%p); } return ans; }