hdu 6058 To my boyfriend (計算貢獻,思維)
阿新 • • 發佈:2017-08-03
時間 () light 區間 frame enc const log --
題意: 給你一個全排列,要你求這個序列的所有區間的第k大的和
思路:比賽的時候一看就知道肯定是算貢獻,也知道是枚舉每個數,然後看他在多少個區間是第K大,然後計算他的貢獻就可以了,但是沒有找到如何在o(k)的時間內找到這k個區間,然後就一直掛機,慘慘慘
感覺官方題解的思路就很棒啊:
我們只要求出對於一個數x左邊最近的k個比他大的和右邊最近k個比他大的,掃一下就可以知道有幾個區間的k大值是x.=
我們考慮從小到大枚舉x,每次維護一個鏈表,鏈表裏只有>=x的數,那麽往左往右找只要暴力跳k次,刪除也是O(1)的。
時間復雜度:O(nk)
代碼:
/** @xigua */ #include <cstdio> #include <cmath> #include <iostream> #include <algorithm> #include <vector> #include <stack> #include <cstring> #include <queue> #include <set> #include <string> #include <map> #include <climits> #define PI acos(-1) using namespace std; typedef long long ll; typedef double db; const int maxn = 5e5 + 5; const int mod = 1e9 + 9; const int mod2 = 1e9 + 7; const int INF = 1e8 + 5; const ll inf = 1e15 + 5; const db eps = 1e-5; const ll hp = 233333; int a[maxn], pos[maxn], pre[maxn], nex[maxn]; ll pp[105], np[105]; void solve() { int n, k; cin >> n >> k; for (int i = 1; i <= n; i++) { scanf("%d", a + i); pos[a[i]] = i; pre[i] = i - 1; nex[i] = i + 1; } pre[0] = -1, nex[n+1] = n + 2; ll ans = 0; for (int i = 1; i <= n; i++) { int t1 = 0, t2 = 0; int curp = pos[i]; for (int j = curp; j >= 0 && t1 <= k; j = pre[j]) { pp[++t1] = j; } for (int j = curp; j <= n + 1 && t2 <= k; j = nex[j]) { np[++t2] = j; } for (int j = 1; j <= t1 - 1; j++) { if (k - j + 2 > t2) continue; ll tmp = (pp[j] - pp[j+1]) * (np[k - j + 2] - np[k - j + 1]); ans += tmp * i; } //刪除當前節點 相當於鏈表 int tp = pre[curp], tn = nex[curp]; nex[tp] = tn, pre[tn] = tp; } cout << ans << endl; } int main() { int t = 1, cas = 1; // freopen("in.txt", "r", stdin); // freopen("out.txt", "w", stdout); // init(); scanf("%d", &t); while(t--) { // printf("Case %d: ", cas++); solve(); } return 0; }
hdu 6058 To my boyfriend (計算貢獻,思維)