abcdeffa's Blog

当局者迷,旁观者清。

0%

GMOJ S5431 【序列操作】

Description

一开始有 $n$ 个非负整数 $h_i$,接下来会进行 $m$ 次操作,第 $j$ 次操作给出一个数 $c_j$,要求你选出 $c_j$ 个大于零的 $h_i$ 并将它们减去 1。问最多可以进行多少轮操作后无法完整地完成操作。

Solution

考虑先将 $h_i$ 排序,然后暴力模拟减的过程。

但是我们发现这样可能会导致某个时刻序列并不单调,从而不能够保证正确性。

于是考虑让它在减的时候保持单调性。

发现如果一次区间减之前有单调性,减完以后却没有了,那么肯定是末尾的一段失去了单调性。

于是考虑把失去单调性的一段挪到数值相同的后一段去减,显然这是等效的。

用数据结构可以快速地满足需求。

智商不够,数据结构来凑。

Code

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
#include <cstdio>
#include <iostream>
#include <algorithm>
#define maxN 1000010
using namespace std;
struct Tree{ int max, tag; } a[maxN << 2];
int p[maxN], q[maxN];
bool cmp (int x, int y)
{
return x > y;
}
int Max (int x, int y)
{
return x > y ? x : y;
}
void pushdown (int now, int l, int r)
{
if(a[now].tag)
{
a[now << 1].max -= a[now].tag;
a[now << 1 | 1].max -= a[now].tag;
a[now << 1].tag += a[now].tag;
a[now << 1 | 1].tag += a[now].tag;
a[now].tag = 0;
}
}
void change (int now, int l, int r, int x, int t)
{
pushdown(now, l, r);
if(l == r)
{
a[now].max = t;
return ;
}
int mid = (l + r) >> 1;
if(x <= mid)
{
change(now << 1, l, mid, x, t);
}
else
{
change(now << 1 | 1, mid + 1, r, x, t);
}
a[now].max = Max(a[now << 1].max, a[now << 1 | 1].max);
}
int query (int now, int l, int r, int L, int R)
{
pushdown(now, l, r);
if(l == L && r == R)
{
return a[now].max;
}
int mid = (l + r) >> 1;
if(R <= mid)
{
return query(now << 1, l, mid, L, R);
}
else if(mid + 1 <= L)
{
return query(now << 1 | 1, mid + 1, r, L, R);
}
else
{
int A = query(now << 1, l, mid, L, mid);
int B = query(now << 1 | 1, mid + 1, r, mid + 1, R);
return Max(A, B);
}
}
void Minus (int now, int l, int r, int L, int R)
{
if(l == L && r == R)
{
a[now].max--;
a[now].tag += 1;
return ;
}
pushdown(now, l, r);
int mid = (l + r) >> 1;
if(R <= mid)
{
Minus(now << 1, l, mid, L, R);
}
else if(mid + 1 <= L)
{
Minus(now << 1 | 1, mid + 1, r, L, R);
}
else
{
Minus(now << 1, l, mid, L, mid);
Minus(now << 1 | 1, mid + 1, r, mid + 1, R);
}
a[now].max = Max(a[now << 1].max, a[now << 1 | 1].max);
}
int main ()
{
int n = 0, m = 0;
scanf("%d %d", &n, &m);
for(int i = 1;i <= n; i++)
{
scanf("%d", &p[i]);
}
sort(p + 1, p + n + 1, cmp);
for(int i = 1;i <= n; i++)
{
change(1, 1, n, i, p[i]);
}
bool flag = false;
for(int i = 1;i <= m; i++)
{
int x = 0;
scanf("%d", &x);
int C = query(1, 1, n, x, x);
if(!C)
{
flag = true;
printf("%d", i - 1);
break;
}
int L = 0, R = 0;
int l = 1, r = n;
while(l < r)
{
int mid = (l + r + 1) >> 1;
if(query(1, 1, n, mid, n) >= C)
{
l = mid;
}
else
{
r = mid - 1;
}
}
R = l;
l = 1, r = n;
while(l < r)
{
int mid = (l + r) >> 1;
if(query(1, 1, n, mid, n) > C)
{
l = mid + 1;
}
else
{
r = mid;
}
}
L = l;
int len = x - (L - 1);
if(L > 1)
{
Minus(1, 1, n, 1, L - 1);
}
Minus(1, 1, n, R - len + 1, R);
}
if(!flag)
{
printf("%d", m);
}
return 0;
}