BZOJ 2431 [HAOI2009]逆序对数列:dp 逆序对
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2431
题意:
给定n,k,问你有多少个由1~n组成的排列,使得逆序对个数恰好为k个。
题解:
表示状态:
dp[i][j] = num of sequences
i:已经用了1~i之间的数(在这一步放了数字i)
j:逆序对个数为j
找出答案:
ans = dp[n][k]
如何转移:
在当前这一步要放数字i。
所以要将i插入一个由1~i-1组成的排列中。
若将i插入位置x(0 <= x <= i-1),则新添的逆序对个数为x。
所以:
dp[i][j] = ∑ dp[i-1][j-x]
即:
dp[i][j] = ∑ dp[i-1][j-i+1 to j]
由于裸dp复杂度为O(N^3) = O(10^9),所以加一个前缀和优化。
边界条件:
dp[1][0] = 1
others = 0
AC Code:
1 // state expression: 2 // dp[i][j] = num of sequences 3 // i: considered number i 4 // j: there is j inversion pairs 5 // 6 // find the answer: 7 // ans = dp[n][k] 8 // 9 // transferring: 10 // dp[i][j] = sigma dp[i-1][j-i+1 to j] 11 // 12 // boundary: 13 // dp[1][0] = 1 14 #include <iostream> 15 #include <stdio.h> 16 #include <string.h> 17 #define MAX_N 1005 18 #define MAX_K 1005 19 #define MOD 10000 20 21 using namespace std; 22 23 int n,t; 24 int dp[MAX_N][MAX_K]; 25 int sum[MAX_N][MAX_K]; 26 27 void read() 28 { 29 cin>>n>>t; 30 } 31 32 void update_sum(int i,int j,int a) 33 { 34 if(j==0) sum[i][j]=a; 35 else sum[i][j]=(sum[i][j-1]+a)%MOD; 36 } 37 38 int query_sum(int i,int x,int y) 39 { 40 if(x==0) return sum[i][y]; 41 else return ((sum[i][y]-sum[i][x-1])%MOD+MOD)%MOD; 42 } 43 44 void solve() 45 { 46 memset(dp,0,sizeof(dp)); 47 memset(sum,0,sizeof(sum)); 48 dp[1][0]=1; 49 for(int i=0;i<=t;i++) 50 { 51 sum[1][i]=1; 52 } 53 for(int i=2;i<=n;i++) 54 { 55 for(int j=0;j<=t;j++) 56 { 57 dp[i][j]=query_sum(i-1,max(0,j-i+1),j); 58 update_sum(i,j,dp[i][j]); 59 } 60 } 61 } 62 63 void print() 64 { 65 cout<<dp[n][t]<<endl; 66 } 67 68 int main() 69 { 70 read(); 71 solve(); 72 print(); 73 }