「Bzoj1112」砖块

Source and Judge

POI2008
Bzoj1112

Problem

「Description」
给出n个数,希望有连续k个是一样的。
可以由两种操作:
1. 一个数-1
2. 一个数+1

求完成任务的最少操作数
「Input」
第一行给出N,K。
下面N行,每行代表这柱砖的高度.
「Output」
最小的动作次数
「Limited conditions」
1≤k≤n≤100000
0≤hi≤1000000
「Sample input」
5 3
3
9
2
3
1
「Sample output」
2
「Sample explanation」

Record

2h

Analysis

请先思考后再展开

等价于,对于一个长度为k的区间,把所有数变成数轴上的点,找到一个位置使所有点到此距离之和最小。
之前在糖果中讲过,这个位置必然是中位数,否则存在使答案更小的办法。

然后搞一个数据结构,维护一下第k大来找中位数,统计答案即可。
然后类似在数轴上滑动的一个窗,从而利用之前的信息而不必重构(显然考试的时候还是忘记啦)
时间复杂度:$O(nlogn)$

考试的时候打了个很挫的主席树,而且没想到可以搞中位数,只想到是抛物线
至于二次函数上三分……不会,好像也蛮慢的

Code1

先来一发无脑splay
好处是值可以灰常大而不受限制,空间也少一个log

请先思考后再展开
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
//Zory in 2018
//*******************头文件*******************
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<stack>
#include<set>
#include<algorithm>
#ifdef WIN32
#define BIGN "%I64d"
#else
#define BIGN "%lld"
#endif
using namespace std;
typedef long long ll;
int mymax(int x,int y) {return x>y?x:y;}
ll mymin(ll x,ll y) {return x<y?x:y;}
int myabs(int x) {return x>0?x:-x;}
//*******************全局常量*******************
const int MAXN=110000;
const int INF=0x3f3f3f3f;
//*******************全局定义*******************
struct Nod
{
int son[2],f;
int c,n;
ll d,sum;
}p[MAXN];
//*******************Splay*******************
void update(int x)
{
p[x].c=p[x].n;p[x].sum=p[x].d*p[x].n;//debug!!
int lc=p[x].son[0],rc=p[x].son[1];
if(lc>0) p[x].c+=p[lc].c,p[x].sum+=p[lc].sum;
if(rc>0) p[x].c+=p[rc].c,p[x].sum+=p[rc].sum;
}
void rotate(int x,int w)
{
int f=p[x].f,ff=p[f].f;

if(p[ff].son[0]==f) p[ff].son[0]=x;
else p[ff].son[1]=x;
p[x].f=ff;

int pson=p[x].son[w];
p[f].son[1-w]=pson;
if(pson>0) p[pson].f=f;

p[x].son[w]=f;
p[f].f=x;

update(f);
update(x);
}
int root;
void splay(int x,int rt)
{
while(p[x].f!=rt)
{
int f=p[x].f,ff=p[f].f;
if(ff==rt)
{
if(p[f].son[0]==x) rotate(x,1);
else rotate(x,0);
}
else
{
if(p[ff].son[0]==f and p[f].son[0]==x) rotate(f,1),rotate(x,1);
else if(p[ff].son[0]==f and p[f].son[1]==x) rotate(x,0),rotate(x,1);
else if(p[ff].son[1]==f and p[f].son[0]==x) rotate(x,1),rotate(x,0);
else if(p[ff].son[1]==f and p[f].son[1]==x) rotate(f,0),rotate(x,0);
}
}
if(rt==0) root=x;
}
int findip(ll d)
{
int x=root;
while(p[x].d!=d)
{
if(d<p[x].d)
{
if(p[x].son[0]>0) x=p[x].son[0];
else break;
}
else
{
if(p[x].son[1]>0) x=p[x].son[1];
else break;
}
}
return x;
}
int cnt=0;
void add(ll d,int f)
{
cnt++;
p[cnt].son[0]=p[cnt].son[1]=-1;
p[cnt].c=p[cnt].n=1;
p[cnt].sum=p[cnt].d=d;

p[cnt].f=f;
if(d<p[f].d) p[f].son[0]=cnt;
else p[f].son[1]=cnt;
}
void insert(ll d)
{
if(!root)
{
add(d,0);
root=cnt;
return;
}

int x=findip(d);
if(p[x].d==d)
{
p[x].n++;
splay(x,0);
}
else
{
add(d,x);
splay(cnt,0);
}
}
void del(ll d)
{
int x=findip(d);
splay(x,0);
if(p[x].n>1)
{
p[x].n--;
update(x);
return;
}

int lc=p[x].son[0],rc=p[x].son[1];
if(lc==-1 and rc>0)
{
root=rc;
p[root].f=0;
}
else if(lc>0 and rc==-1)
{
root=lc;
p[root].f=0;
}
else
{
int t=rc;
while(p[t].son[0]>0) t=p[t].son[0];
splay(t,root);p[t].f=0;root=t;
p[t].son[0]=lc;p[lc].f=t;
update(root);//debug
}
}
ll findk(int k)
{
int x=root;
while(1)
{
int lc=p[x].son[0],lcc=(lc>0)?p[lc].c:0;
if(k<=lcc) x=lc;
else if(k>lcc+p[x].n) k-=lcc+p[x].n,x=p[x].son[1];
else break;
}
splay(x,0);
return p[x].d;
}
//*******************实现*******************
ll solve(int k)
{
ll mid=findk(k/2+1);
int lc=p[root].son[0],rc=p[root].son[1];
ll sm1=mid*p[lc].c,sm2=p[lc].sum;
ll bg1=mid*p[rc].c,bg2=p[rc].sum;
ll ans=0;
if(lc>0) ans+=sm1-sm2;
if(rc>0) ans+=bg2-bg1;
return ans;//debug 没有其中儿子
}
//*******************主函数*******************
int h[MAXN];
int main()
{
int n,k;scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&h[i]);

for(int i=1;i<=k;i++) insert(h[i]);
ll ans=solve(k);
for(int r=k+1;r<=n;r++)
{
insert(h[r]);
del(h[r-k]);
ans=mymin(ans,solve(k));
}
printf(BIGN,ans);
}

Code2

是不是觉得巨长?
其实有代码又短,常数又小的树状数组
小心空间复杂度

请先思考后再展开
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
//Zory in 2018
//*******************头文件*******************
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<stack>
#include<set>
#include<algorithm>
#ifdef WIN32
#define BIGN "%I64d"
#else
#define BIGN "%lld"
#endif
using namespace std;
typedef long long ll;
int mymax(int x,int y) {return x>y?x:y;}
ll mymin(ll x,ll y) {return x<y?x:y;}
int myabs(int x) {return x>0?x:-x;}
//*******************全局常量*******************
const int MAXN=110000;
const int MAXNUM=1000001;//1000000+1
const int INF=0x3f3f3f3f;
//*******************全局定义*******************
int bin[21];
ll cnt[MAXNUM],sum[MAXNUM];//次数、和
//*******************树状数组*******************
int lowbit(int x) {return x&(-x);}
void add(int p,int c,ll *s)
{
while(p<=MAXNUM) s[p]+=c,p+=lowbit(p);
}
ll getsum(int p,ll *s)
{
ll ans=0;
while(p>=1) ans+=s[p],p-=lowbit(p);
return ans;
}

ll findk(int k)
{
int now=0,tk=0;
for(int i=20;i>=0;i--)//逆向求和
{
now+=bin[i];
if(now<=MAXNUM and tk+cnt[now]<k) tk+=cnt[now];
else now-=bin[i];
}
return now+1;//「小于k数量」+1
}
//*******************实现*******************
void insert(ll d)
{
add(d,1,cnt);
add(d,d,sum);
}
void del(ll d)
{
add(d,-1,cnt);
add(d,-d,sum);
}
ll solve(int k)
{
ll mid=findk(k/2+1);
ll sm1=mid*getsum(mid-1,cnt),sm2=getsum(mid-1,sum);
ll bg1=mid*(getsum(MAXNUM,cnt)-getsum(mid,cnt)),bg2=getsum(MAXNUM,sum)-getsum(mid,sum);
return (sm1-sm2)+(bg2-bg1);
}
//*******************主函数*******************
int h[MAXN];
int main()
{
bin[0]=1;for(int i=1;i<=20;i++) bin[i]=bin[i-1]<<1;

int n,k;scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&h[i]),h[i]++;//debug 有0

for(int i=1;i<=k;i++) insert(h[i]);
ll ans=solve(k);
for(int r=k+1;r<=n;r++)
{
insert(h[r]);
del(h[r-k]);
ans=mymin(ans,solve(k));
}
printf(BIGN,ans);
}

本文基于 知识共享署名-相同方式共享 4.0 国际许可协议发布
本文地址:http://zory.ink/posts/2ed3.html
转载请注明出处,谢谢!