BZOJ1367 [Baltic2004]sequence

现学的左偏树。。。这可是道可并堆的好题目。

首先我们考虑z不减的情况:

我们发现对于一个区间[l, r],里面是递增的,则对于此区间最优解为z[i] = t[i];

如果里面是递减的,z[l] = z[l + 1] = ... = z[r] = 这段数的中位数,不妨叫做w。(此处我们定义中位数为第(r - l + 1) / 2大的数,因为这并不影响结果)

而其实递增可以转化为每一段只有一个点,就等价于递减了。

那么我们把原数列分段,每段都是递减的,而每一段的z都是那段的中位数w。这样就找到了最优解。(证略)

这样就有了解法:

(1)新加入一个数至数列末端,先把它当成单独一段

(2)每次看最后一段的w[tot]和前一段的w[tot - 1],若w[tot] < w[tot - 1],则说明合并可以更优,合并这两段。

(3)最后计算ans

这之中还有一个问题:z[i]不是不减而是递增。有个巧妙地办法:让z[i](新) = z[i](老) - i即可,这样就保证了z的递增性质。

 1 /**************************************************************
 2     Problem: 1367
 3     User: rausen
 4     Language: C++
 5     Result: Accepted
 6     Time:5284 ms
 7     Memory:59400 kb
 8 ****************************************************************/
 9
10 #include <cstdio>
11 #include <cstring>
12 #include <algorithm>
13
14 using namespace std;
15 const int N = 1500000;
16
17 struct heap{
18     int v, l, r, dep;
19 }h[N];
20 int l[N], r[N], cnt[N], num[N], root[N];
21 int z[N];
22 int n, tot, Cnt;
23
24 inline int read(){
25     int x = 0, sgn = 1;
26     char ch = getchar();
27     while (ch < ‘0‘ || ch > ‘9‘){
28         if (ch == ‘-‘) sgn = -1;
29         ch = getchar();
30     }
31     while (ch >= ‘0‘ && ch <= ‘9‘){
32         x = x * 10 + ch - ‘0‘;
33         ch = getchar();
34     }
35     return sgn * x;
36 }
37
38 int new_heap(int x){
39     h[++Cnt].v = x;
40     h[Cnt].l = h[Cnt].r = h[Cnt].dep = 0;
41     return Cnt;
42 }
43
44 int Merge(int x, int y){
45     if (!x || !y) return x + y;
46     if (h[x].v < h[y].v)
47         swap(x, y);
48     h[x].r = Merge(h[x].r, y);
49     if (h[h[x].l].dep < h[h[x].r].dep)
50         swap(h[x].l, h[x].r);
51     h[x].dep = h[h[x].r].dep + 1;
52     return x;
53 }
54
55 int Top(int x){
56     return h[x].v;
57 }
58
59 int Pop(int x){
60     return Merge(h[x].l, h[x].r);
61 }
62
63 int main(){
64     n = read();
65     for (int i = 1; i <= n; ++i)
66         z[i] = read() - i;
67
68     for (int i = 1; i <= n; ++i){
69         ++tot;
70         root[tot] = new_heap(z[i]);
71         cnt[tot] = 1, num[tot] = 1;
72         l[tot] = i, r[tot] = i;
73
74         while (tot > 1 && Top(root[tot]) < Top(root[tot - 1])){
75             --tot;
76             root[tot] = Merge(root[tot], root[tot + 1]);
77             num[tot] += num[tot + 1], cnt[tot] += cnt[tot + 1], r[tot] = r[tot + 1];
78             for(; cnt[tot] * 2 > num[tot] + 1; --cnt[tot])
79                 root[tot] = Pop(root[tot]);
80         }
81     }
82
83     long long ans = 0;
84     for (int i = 1; i <= tot; ++i)
85         for (int j = l[i], w = Top(root[i]); j <= r[i]; ++j)
86             ans += abs(z[j] - w);
87     printf("%lld\n", ans);
88     return 0;
89 }

(p.s. 真是写的矬死了。。。越优化又慢,都醉了)

时间: 2024-11-10 12:42:03

BZOJ1367 [Baltic2004]sequence的相关文章

bzoj1367 [Baltic2004]sequence [左偏树]

新博客宣传一发http://shijieyywd.com/?p=73 Description 给定一个序列t1,t2,...,tn,求一个递增序列z1<z2<...<zn, 使得R=|t1?z1|+|t2?z2|+...+|tn?zn|的值最小.本题中,我们只需要求出这个最小的R值. Input 第1行为一个整数n(1<=n<=106), 第2行到第n + 1行,每行一个整数,第k + 1行为tk(0<=tk<=2?109). Output 一个整数R Sampl

可并堆试水--BZOJ1367: [Baltic2004]sequence

n<=1e6个数,把他们修改成递增序列需把每个数增加或减少的总量最小是多少? 方法一:可以证明最后修改的每个数一定是原序列中的数!于是$n^2$DP(逃) 方法二:把$A_i$改成$A_i-i$,变论文题:论文 大概证明是这样的:考虑合并两个区间的答案,假如一个区间答案是{u,u,u,--,u},另一个是{v,v,v,--,v},那合并之后,如果u<=v最优就{u,u,--,u,v,--,v}:如果u>v,假设最优是 {b1,b2,--,bn,bn+1,--,bm},那么一定有bn<

【BZOJ1367】[Baltic2004]sequence 左偏树

[BZOJ1367][Baltic2004]sequence Description Input Output 一个整数R Sample Input 7 9 4 8 20 14 15 18 Sample Output 13 HINT 所求的Z序列为6,7,8,13,14,15,18.R=13 题解:详见论文 然而本题要求z[i]严格递增,那就让所有t[i]-=i就好了 #include <cstdio> #include <cstring> #include <iostrea

【bzoj1367】[Baltic2004]sequence

2016-05-31 17:31:26 1 #include<bits/stdc++.h> 2 #define inf 1000000000 3 #define ll long long 4 #define N 1000005 5 using namespace std; 6 int read(){ 7 int x=0,f=1;char ch=getchar(); 8 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 9 wh

bzoj 1367: [Baltic2004]sequence

1367: [Baltic2004]sequence Time Limit: 20 Sec  Memory Limit: 64 MB Description Input Output 一个整数R Sample Input 7 9 4 8 20 14 15 18 Sample Output 13 HINT 所求的Z序列为6,7,8,13,14,15,18.R=13 详细证明请看IOI2005国家集训队论文  黄源河 https://wenku.baidu.com/view/20e9ff18964b

【BZOJ-1367】sequence 可并堆+中位数

1367: [Baltic2004]sequence Time Limit: 20 Sec  Memory Limit: 64 MBSubmit: 932  Solved: 348[Submit][Status][Discuss] Description Input Output 一个整数R Sample Input 7 9 4 8 20 14 15 18 Sample Output 13 HINT 所求的Z序列为6,7,8,13,14,15,18.R=13 Source Solution 论文

bzoj 1367: [Baltic2004]sequence(中位数+可并堆)

1367: [Baltic2004]sequence Time Limit: 20 Sec  Memory Limit: 64 MB Submit: 935  Solved: 351 [Submit][Status][Discuss] Description Input Output 一个整数R Sample Input 7 9 4 8 20 14 15 18 Sample Output 13 HINT 所求的Z序列为6,7,8,13,14,15,18. R=13 Source [Submit]

【BZOJ 1367】 [Baltic2004]sequence

1367: [Baltic2004]sequence Time Limit: 20 Sec  Memory Limit: 64 MB Submit: 631  Solved: 215 [Submit][Status] Description Input Output 一个整数R Sample Input 7 9 4 8 20 14 15 18 Sample Output 13 HINT 所求的Z序列为6,7,8,13,14,15,18. R=13 左偏树. 详细题解(在P13). 这个题解求的z

1367: [Baltic2004]sequence

1367: [Baltic2004]sequence Time Limit: 20 Sec  Memory Limit: 64 MB Submit: 1090  Solved: 432 [Submit][Status][Discuss] Description Input Output 一个整数R Sample Input 7 9 4 8 20 14 15 18 Sample Output 13 HINT 所求的Z序列为6,7,8,13,14,15,18. R=13 Source [题解]: 详