洛谷 P4280 bzoj1786 [AHOI2008]逆序對(dp)
阿新 • • 發佈:2019-01-09
題面
題目大意:
- 給你一個長度為\(n\)的序列,元素都在\(1-k\)之間,有些是\(-1\),讓你把\(-1\)也變成\(1-k\)之間的數,使得逆序對最多,求逆序對最少是多少
- \(n<=10000,k<=100\)
題解
結論:
填的數是不下降的
證明:
假設相鄰的兩個-1的位置是(x,y)(a[x]<=a[y]); 如果交換x,y; 對1-x和y-n中的數顯然沒有影響. 對x-y中大於max(a[x],a[y])和小於min(a[x],a[y])的數顯然也沒有影響. 但是對x-y中a[x]-a[y]的數,逆序對數顯然增加了. 所以交換x,y只會導致逆序對數不降. 所以-1位置的數一定是單調不降的.
\(l[i][j]\)表示前\(i\)個數有多少大於\(j\)的數
\(r[i][j]\)表示後\(i\)個數有多少小於\(j\)的數
我們把-1位置弄出來\(dp\)
\(f[i][j]\)表示第\(i\)位填\(j\),逆序對個數最少是多少
\(b[i]表示第i個空在原序列的位置\)
假設\(i\)位填\(t\)
則
\(f[i][t] = min(f[i-1][z]+l[b[i]][t]+r[b[i]][t])\)
這樣複雜度是\(O(n*k^2)\)
- 注意還要加上沒填數時的逆序對
可以發現\(f[i-1][z]\) 可以用一個字首最小來優化
Code
#include<bits/stdc++.h> #define LL long long #define RG register using namespace std; template<class T> inline void read(T &x) { x = 0; RG char c = getchar(); bool f = 0; while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') c = getchar(), f = 1; while (c >= '0' && c <= '9') x = x*10+c-48, c = getchar(); x = f ? -x : x; return ; } template<class T> inline void write(T x) { if (!x) {putchar(48);return ;} if (x < 0) x = -x, putchar('-'); int len = -1, z[20]; while (x > 0) z[++len] = x%10, x /= 10; for (RG int i = len; i >= 0; i--) putchar(z[i]+48);return ; } const int N= 10010; int cnt, l[N][110], r[N][110], a[N], b[N]; LL f[N][110], g[N][110]; int main() { //freopen(".in", "r", stdin); //freopen(".out", "w", stdout); int n, k; read(n); read(k); for (int i = 1; i <= n; i++) { read(a[i]); if (a[i] == -1) b[++cnt] = i; } for (int i = 1; i <= n; i++) for (int j = 1; j <= k; j++) { l[i][j] = l[i-1][j]; if (a[i] > j) l[i][j]++; } for (int i = n; i >= 1; i--) for (int j = 1; j <= k; j++) { r[i][j] = r[i+1][j]; if (a[i] < j && a[i] != -1) r[i][j]++; } for (int i = 1; i <= cnt; i++) g[i][0] = 1ll << 60; for (int i = 1; i <= cnt; i++) for (int j = 1; j <= k; j++) { f[i][j] = g[i-1][j]+l[b[i]][j]+r[b[i]][j]; g[i][j] = min(g[i][j-1], f[i][j]); } LL ans = 1ll << 60; for (int i = 1; i <= k; i++) ans = min(ans, f[cnt][i]); for (int i = 1; i <= n; i++) ans += l[i][a[i]]; printf("%lld\n", ans); return 0; }