题目链接:http://codeforces.com/gym/100269
题意:
现在有n个人,s个位置和你可以划分长k个区域

你可以把s个位置划分成k个区域,这样每个人坐下你的代价是该区域内,在你之前比你小的人的数量

问你怎么划分这s个位置(当然,每个区域必须是连续的),才能使得总代价最小

输出代价

话说这个题真是读了好久好久

解法:

数据范围1000,显然的dp

dp[i][j]表示第i个位置是第j个区域的结尾,然后暴力转移就好了

用树状数组预处理sum[i][j],表示第i个位置和第j个位置划分在一起的代价是多少

复杂度 O(m*m*k + m*m*logm)

//CF GYM 100269

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1050;
int dp[maxn][55];///dp[i][j]表示第i个位置是第j个区域的结尾
int sum[maxn][maxn];///表示第i个位置和第j个位置划分在一起的代价是多少
int r[maxn];
int n, m, k;

vector <int> E[maxn];

namespace BIT{
    int c[maxn];
    inline int lowbit(int x){return x&-x;}
    inline void update(int i, int v){for(; i < maxn; i += lowbit(i)) c[i] += v;}
    inline int query(int i){int res = 0; for(; i; i-=lowbit(i)) res += c[i]; return res;}
} using namespace BIT;

int main()
{
    freopen("flight.in","r",stdin);
    freopen("flight.out","w",stdout);
    scanf("%d%d%d", &n, &m, &k);
    for(int i = 1; i <= n; i++){
        scanf("%d", &r[i]);
        E[r[i]-1].push_back(i);
    }
    for(int i = 0; i < m; i++){
        memset(c, 0, sizeof(c));
        for(int j = i; j < m; j++){
            if(i != j) sum[i][j] = sum[i][j-1];
            for(int t = 0; t < E[j].size(); t++){
                sum[i][j] += query(E[j][t]);
            }
            for(int t = 0; t < E[j].size(); t++){
                update(E[j][t], 1);
            }
        }
    }
    memset(dp, 0x3f, sizeof(dp));
    dp[0][0] = 0;
    for(int i = 0; i < m; i++){
        for(int j = 0; j < k; j++){
            if(dp[i][j] == 0x3f) continue;
            for(int t = i; t < m; t++){
                dp[t+1][j+1] = min(dp[t+1][j+1], dp[i][j] + sum[i][t]);
            }
        }
    }
    printf("%d\n", dp[m][k]);
    return 0;
}