4/9上午考试

4/9上午考试

四月 09, 2022
T1

来源:CF802C Heidi and Library

题意:

  你有一个可以放 $K$ 本书的书架,第 $i$ 天要求书架上有第 $a_i$ 种书,购买第 $i$ 种书的价格是 $c_i$,求满足 $n$ 天要求的最小花费。

$1\leq n,k\leq 80$

题解:

   看见 $n , k$ 的范围很小,可以想到费用流,但是正着想每天买什么想不出来,所以不妨倒着想,先全卖掉,然后每天卖掉没用的。

  首先拆点, $i \rightarrow i + n$ 连一条费用为 0 , 流量为 1 的边。

  $S \rightarrow i$ 连一条费用为 $c_{a_i}$ ,流量为 1 的边。

  $i + 1 \rightarrow i$ 连一条费用为 0 ,流量为 $k - 1$ 的边。

  $i - 1\rightarrow j + n$ 连一条费用为 $-c_i$ , 流量为 1 的边,$j$ 为 $a_i$ 上一次出现的位置。

  $i + n \rightarrow T $ 连一条费用为 0 , 流量为 1 的边。

  最后直接跑费用流即可。

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
/* Written by Rikka__  */
/* 手持两把锟斤拷,口中疾呼烫烫烫 */
#include<bits/stdc++.h>
using namespace std;
#define _ =read()
inline int read(){
int r = 0 , w = 1;
char ch = getchar();
while(ch > '9' || ch < '0') {if(ch == '-') w = -1; ch = getchar();}
while(ch >='0' && ch <='9') {r = r * 10 + ch -'0'; ch = getchar();}
return r * w;
}
namespace FileIO{
string InFile;
string OutFile;
void File(string s){
InFile = s + ".in";
OutFile = s + ".out";
freopen(InFile.data() , "r" , stdin);
freopen(OutFile.data() , "w" , stdout);
}
}
const int Maxn = 10000;
int head[Maxn];
int cnt = 1;
struct Vollerei{
int to;
int nxt;
int cost;
int flow;
int from;
};
Vollerei *ed[Maxn << 1];
void add(int x , int y , int flow , int cost){
ed[++cnt] = new Vollerei;
ed[cnt]->to = y;
ed[cnt]->from = x;
ed[cnt]->flow = flow;
ed[cnt]->cost = cost;
ed[cnt]->nxt = head[x];
head[x] = cnt;
ed[++cnt] = new Vollerei;
ed[cnt]->to = x;
ed[cnt]->from = y;
ed[cnt]->flow = 0;
ed[cnt]->cost = -cost;
ed[cnt]->nxt = head[y];
head[y] = cnt;
}
int n;
int k;
int a[Maxn];
int c[Maxn];
int pre[Maxn];
int dis[Maxn];
int flow[Maxn];
int lst[Maxn];
int vis[Maxn];
int S , T;
inline int spfa(int s , int t){
memset(dis , 0x3f , sizeof dis);
memset(flow , 0x3f , sizeof flow);
memset(vis , 0 , sizeof vis);
queue<int> q;
q.push(s) , dis[s] = 0 , vis[s] = 1 , pre[t] = -1;
while(!q.empty()){
int x = q.front();
vis[x] = 0 , q.pop();
for(int i = head[x] ; i ; i = ed[i]->nxt){
int to = ed[i]->to;
int fw = ed[i]->flow;
int ct = ed[i]->cost;
if(fw <= 0) continue;
if(dis[to] > dis[x] + ct){
dis[to] = dis[x] + ct;
pre[to] = x;
lst[to] = i;
flow[to] = min(flow[x] , fw);
if(vis[to] == 0) vis[to] = 1 , q.push(to);
}
}
}
return pre[t] != -1;
}
inline int Mincost_Maxflow(){
int Mincost = 0;
int Maxflow = 0;
while(spfa(S , T)){
int cur = T;
Maxflow += flow[T];
Mincost += flow[T] * dis[T];
while (cur != S) {
ed[lst[cur]]->flow -= flow[T];
ed[lst[cur] ^ 1]->flow += flow[T];
cur = pre[cur];
}
}
return Mincost;
}
int before[Maxn];
signed main(){
// FileIO::File("arcoss");
n _ , k _;
for(int i = 1 ; i <= n ; i++) a[i] _;
for(int i = 1 ; i <= n ; i++) c[i] _;
S = 0 , T = 2 * n + 1;
for(int i = 1 ; i <= n ; i++) add(i , i + n , 1 , 0);
for(int i = 1 ; i <= n ; i++) add(i + n , T , 1 , 0);
for(int i = 1 ; i <= n ; i++) add(S , i , 1 , c[a[i]]);
for(int i = 1 ; i <= n ; i++){
if(before[a[i]]) add(i - 1 , before[a[i]] + n , 1 , -c[a[i]]);
before[a[i]] = i;
}
for(int i = 1 ; i < n ; i++) add(i , i + 1 , k - 1 , 0);
cout << Mincost_Maxflow();
return 0;
}

T2

来源:CF1178G The Awesomest Vertex

题意:

  给定一棵根为 1 的有根树,每个节点有两个权值 $a_i$ 和 $b_i$ ,定义 $anc(x)$ 为 $x$ 的祖先的集合(包括自己),则一个节点 $x$ 的价值计算方式如下:

  维护两种操作:

    1.x kx 加上 k

    2. x 输出以 x 为根的子树中所有节点最大的价值。

题解:

  首先把树按照 dfs 序拍成序列,问题变成了区间的 $a$ 加上一个值,求区间的 $|a| \times b$ 最大值

  发现绝对值比较烦,考虑去求 $max(a \times b , -a\times b)$ 。

  分块的维护比较简单,所以考虑分块,在块内维护上面的最大值。修改的时候直接整块打标记,发现对于整块求的东西变成了 $(a +tag) \times b = b \times tag +a\times b$ ,直接块内维护凸包,对于散块直接重构。

  求答案直接在凸包上面二分,复杂度$O(n\sqrt n \log n)$

  但是还可以优化掉一个 log,这个 log 来自于对于散块重构时的排序 , 整块查询时的二分。

  散块的 log 比较好去掉,因为所有的线的斜率(也就是 $b$ ) 都是不变的,所以只需要在一开始的时候排序即可。

  对于整块二分的 log,我们发现每次修改加的数都是正的,所以如果 tag 不清零的话是单调不降的,所以我们直接搞一个指针,每次给整块修改时尽量后移,给零散块修改时把指针重置,询问时直接调用就行了。

  移动指针的复杂度是均摊 $O(1)$ 的。

  证明:

    指针右移本质上就是坐标右移,而且指针每右移一次指针右边都至少少一条线,故每个块总复杂度就是 $O(size)$,均摊下来就是 $O(1)$ 了。

    再加入零散块操作,由于加的都是正数,所以涉及到的线比块内其它的线加的更多,也就相当于往左移更多。此时重构块时,实际上就是把所有线和指针都往左移一段后,将操作涉及到的线继续往左移,所以指针右边的线还是不会变多。

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
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
/* Written by Rikka__  */
/* 手持两把锟斤拷,口中疾呼烫烫烫 */
#include<bits/stdc++.h>
using namespace std;
#define _ =read()
#define int long long
inline int read(){
int r = 0 , w = 1;
char ch = getchar();
while(ch > '9' || ch < '0') {if(ch == '-') w = -1; ch = getchar();}
while(ch >='0' && ch <='9') {r = r * 10 + ch -'0'; ch = getchar();}
return r * w;
}
namespace FileIO{
string InFile;
string OutFile;
void File(string s){
InFile = s + ".in";
OutFile = s + ".out";
freopen(InFile.data() , "r" , stdin);
freopen(OutFile.data() , "w" , stdout);
}
}
const int Maxn = 801000;
const int siz = 600;
const int num = 1200;
int n;
int q;
struct Line{
int k;
int b;
int id;
friend bool operator < (Line a , Line b){
return a.k < b.k;
}
};
inline double K(Line x , Line y){
return 1.0 * (x.b - y.b) / (y.k - x.k);
}
Line vec[num][siz];
Line Hull[num][siz];
struct Vollerei{
int to;
int nxt;
};
Vollerei *ed[Maxn << 1];
int head[Maxn];
void add(int x , int y){
static int cnt = 0;
ed[++cnt] = new Vollerei;
ed[cnt]->to = y;
ed[cnt]->nxt = head[x];
head[x] = cnt;
}
int Idx;
int in[Maxn];
int out[Maxn];
int a[Maxn];
int A[Maxn];
int suma[Maxn];
int b[Maxn];
int B[Maxn];
int sumb[Maxn];
int L[Maxn];
int R[Maxn];
int bel[Maxn];
int Id[Maxn];
int psiz[Maxn];
int hsiz[Maxn];
int tag[Maxn];
int block_siz;
void dfs(int x , int fa){
suma[x] = suma[fa] + A[x];
sumb[x] = sumb[fa] + B[x];
in[x] = ++Idx;
a[Idx] = suma[x] , b[Idx] = sumb[x];
for(int i = head[x] ; i ; i = ed[i]->nxt){
int to = ed[i]->to;
dfs(to , x);
}
out[x] = Idx;
}
void Move(int x){
while(Id[x] < hsiz[x] && K(Hull[x][Id[x]] , Hull[x][Id[x] + 1]) <= tag[x]) Id[x]++;
}
void build(int x){
hsiz[x] = 0;
for(int i = 1 ; i <= psiz[x] ; i++){
if(a[vec[x][i].id] >= 0 && vec[x][i].k < 0) continue;
bool flag = 0;
int tmp;
while((tmp = hsiz[x]) && Hull[x][tmp].k == vec[x][i].k){
if(Hull[x][tmp].b < vec[x][i].b) hsiz[x]--;
else{
flag = 1;
break;
}
}
if(flag) continue;
while((tmp = hsiz[x]) > 1 && K(Hull[x][tmp - 1] , vec[x][i]) <= K(Hull[x][tmp - 1] , Hull[x][tmp])) hsiz[x]--;
Hull[x][++hsiz[x]] = vec[x][i];
}
Id[x] = 1;
}
void push_down(int x){
for(int i = 1 ; i <= psiz[x] ; i++){
int k = vec[x][i].k , b = vec[x][i].b;
vec[x][i] = Line{k , b + k * tag[x] , vec[x][i].id};
}
for(int i = L[x] ; i <= R[x] ; i++) a[i] += tag[x];
tag[x] = 0 , Id[x] = 1;
}
void init(){
block_siz = max(sqrt(n / 6) , 1.0); //块长取 sqrt(n / 6) 最快
for(int i = 1 ; i <= n ; i++){
bel[i] = i / block_siz;
if(bel[i] != bel[i - 1]){
L[bel[i]] = i;
if(i != 1) R[bel[i - 1]] = i - 1;
}
}
R[bel[n]] = n;
for(int i = 1 ; i <= n ; i++) b[i] = b[i] < 0 ? -b[i] : b[i];

for(int i = 1 ; i <= n ; i++){
vec[bel[i]][++psiz[bel[i]]] = Line{b[i] , a[i] * b[i] , i};
vec[bel[i]][++psiz[bel[i]]] = Line{-b[i] , -a[i] * b[i] , i};
}
for(int i = bel[1] ; i <= bel[n] ; i++) sort(vec[i] + 1 , vec[i] + psiz[i] + 1);
for(int i = bel[1] ; i <= bel[n] ; i++) build(i);
}
void update(int x , int l , int r , int k){
push_down(x);
for(int i = 1 ; i <= psiz[x] ; i++){
int id = vec[x][i].id;
if(l <= id && id <= r) vec[x][i].b += vec[x][i].k * k;
}
for(int i = l ; i <= r ; i++) a[i] += k;
build(x);
}
void update(int l , int r , int k){
if(bel[l] == bel[r]) {
update(bel[l] , l , r , k);
return;
}
update(bel[l] , l , R[bel[l]] , k);
update(bel[r] , L[bel[r]] , r , k);
for(int i = bel[l] + 1 ; i <= bel[r] - 1 ; i++) tag[i] += k;
}
int query(int x , int l , int r){
int ans = 0;
for(int i = l ; i <= r ; i++) {
int s = abs(a[i] + tag[x]) * b[i];
ans = max(ans , s);
}
return ans;
}
int query(int l , int r){
if(bel[l] == bel[r]) return query(bel[l] , l , r);
int ans = max(query(bel[l] , l , R[bel[l]]) , query(bel[r] , L[bel[r]] , r));
for(int i = bel[l] + 1 ; i <= bel[r] - 1 ; i++){
Move(i);
int k = Hull[i][Id[i]].k , b = Hull[i][Id[i]].b;
int t = tag[i];
ans = max(ans , k * t + b);
}
return ans;
}
signed main(){
// FileIO::File("faraway");
n _ , q _;
for(int i = 2 ; i <= n ; i++) {
int x _;
add(x , i);
}
for(int i = 1 ; i <= n ; i++) A[i] _;
for(int i = 1 ; i <= n ; i++) B[i] _;
dfs(1 , 0);
init();
while(q --> 0){
int opt _ , x _;
if(opt == 1){
int k _; , k);
}
update(in[x] , out[x]
else {
cout << query(in[x] , out[x]) << "\n";
}
}
getchar();
return 0;
}


T3

来源:LOJ#6206. 迷失的地球

题意:

  太长了,自己看原题吧QAQ

题解:

  dark模拟,没什么好说的,照着题面模拟就行了。

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
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
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236

/* Written by Rikka__ */
/* ✧*。٩(ˊᗜˋ*)و✧*。 */
#include<bits/stdc++.h>
using namespace std;
#define _ =read()
inline int read(){
int r = 0 , w = 1;
char ch = getchar();
while(ch > '9' || ch < '0') {if(ch == '-') w = -1; ch = getchar();}
while(ch >='0' && ch <='9') {r = r * 10 + ch -'0'; ch = getchar();}
return r * w;
}
namespace FileIO{
string InFile;
string OutFile;
void File(string s){
InFile = s + ".in";
OutFile = s + ".out";
freopen(InFile.data() , "r" , stdin);
freopen(OutFile.data() , "w" , stdout);
}
}
typedef set<int>::iterator Type;
const int Maxn = 20;
const int dirx[] = {0 , 1 , 0 , -1};
const int diry[] = {1 , 0 , -1 , 0};
struct Point{
int x , y;
friend bool operator == (Point a , Point b){
return a.x == b.x && a.y == b.y;
}
};
Point St;
Point Ed;
Point ar[Maxn * Maxn];
struct fort{
Point pos;
int dmg;
int dis;
int type;
int loc;
};
fort T[Maxn];
fort t[Maxn];
struct enemy{
int bld;
int spd;
int rk;
int mark;
int bel;
friend bool operator < (enemy a , enemy b){
if(a.bld != b.bld) return a.bld < b.bld;
else return a.rk < b.rk;
}
};
enemy E[Maxn * Maxn];
struct comp{
bool operator ()(int a , int b){
return E[a] < E[b];
}
};
int n;
int m;
int tnum;
int head;
int top;
int q;
int Len;
int tot = 10;
int Round;
int dam[Maxn * Maxn];
int ans[Maxn * Maxn];
int tmp[Maxn];
char str[Maxn];
bool Mp[Maxn][Maxn];
set<int , comp> s[Maxn * Maxn];
vector<int> loc[Maxn * Maxn];
bool in(Point x){
return x.x <= n && x.y <= m && x.x >= 1 && x.y >= 1;
}
inline int dis(Point x , Point y){
return (x.x - y.x) * (x.x - y.x) + (x.y - y.y) * (x.y - y.y);
}
void dfs(Point x , int p , Point last){
if(x == Ed){
Len = p - 1;
return;
}
Point y;
for(int i = 0 ; i < 4 ; i++){
y.x = x.x + dirx[i];
y.y = x.y + diry[i];
if(y == last) continue;
if(in(y) && Mp[y.x][y.y]){
ar[++p] = y;
dfs(y , p , x);
break;
}
}
}
void Init() {
n _ , m _;
for(int i = 1 ; i <= n ; i++){
scanf("%s" , str + 1);
for(int j = 1 ; j <= m ; j++){
if(str[j] == 'S') St.x = i , St.y = j , Mp[i][j] = 1;
else if (str[j] == 'T') Ed.x = i , Ed.y = j , Mp[i][j] = 1;
else if (str[j] == '0') Mp[i][j] = 1;
else if(str[j] > '0' && str[j] <= '9') tmp[++top] = str[j] - '0' , t[top].pos.x = i , t[top].pos.y = j;
}
}
tnum _;
for(int i = 1 ; i <= tnum ; i++) T[i].dmg _ , T[i].dis _ , T[i].type _ , T[i].dis *= T[i].dis , T[i].loc = 0;
q _;
for(int i = 1 ; i <= q ; i++) E[i].bld _ , E[i].spd _ , E[i].rk = i , E[i].bel = 0 , E[i].mark = 0;
ar[1] = St;
dfs(St , 1 , Point{0 , 0});
for(int i = 1 ; i <= top ; i++){
t[i].dmg = T[tmp[i]].dmg;
t[i].dis = T[tmp[i]].dis;
t[i].type = T[tmp[i]].type;
t[i].loc = T[tmp[i]].loc;
}
}
void summon(){
if(head == q) return;
s[1].insert(++head);
E[head].bel = 1;
}
void locate(){
for(int i = 1 ; i <= top ; i++){
if(t[i].loc && dis(t[i].pos , ar[E[t[i].loc].bel]) <= t[i].dis) continue;
t[i].loc = 0;
for(int j = Len ; j ; j--){
if(!s[j].empty() && dis(ar[j] , t[i].pos) <= t[i].dis){
t[i].loc = *s[j].begin();
break;
}
}
}
}
void calc(Point a , Point b , int &A , int &B , int &C){
int x1 = a.x , x2 = b.x , y1 = a.y , y2 = b.y;
A = y1 - y2 , B = x2 - x1 , C = -(A * x1 + B * y1);
}
bool ifyoucan(Point p , int A , int B , int C){
int x = p.x , y = p.y;
return (A * x + B * y + C) * (A * x + B * y + C) <= (A * A + B * B);
}
void attack(){
memset(dam , 0 , sizeof dam);
for(int i = 1 ; i <= top ; i++){
int x = t[i].loc;
if(x == 0) continue;
if(t[i].type == 0) dam[x] += t[i].dmg;
if(t[i].type == 1) {
int j = E[x].bel;
for(Type it = s[j].begin() ; it != s[j].end() ; it++) dam[*it] += t[i].dmg;
if(j > 1){
for(Type it = s[j - 1].begin() ; it != s[j - 1].end() ; it++) dam[*it] += t[i].dmg;
}
if(j < Len){
for(Type it = s[j + 1].begin() ; it != s[j + 1].end() ; it++) dam[*it] += t[i].dmg;
}
}
if(t[i].type == 2){
int a , b , c;
calc(t[i].pos , ar[E[x].bel] , a , b , c);
for(int j = 1 ; j <= Len ; j++){
if(ifyoucan(ar[j] , a , b , c)){
for(Type it = s[j].begin() ; it != s[j].end() ; it++) dam[*it] += t[i].dmg;
}
}
}
if(t[i].type == 3) dam[x] += t[i].dmg , E[x].mark = 1;
}
}
void check_damage(){
for(int i = 1 ; i <= head ; i++){
if(E[i].bel == 0) continue;
if(E[i].bld <= dam[i]) s[E[i].bel].erase(i) , E[i].bel = 0 , ans[i] = Round;
else E[i].bld -= dam[i];
}
for(int i = 1 ; i <= top ; i++){
if(!E[t[i].loc].bel) t[i].loc = 0;
}
}
void move(){
for(int i = 1 ; i <= head ; i++){
if(E[i].bel == 0) continue;
if(E[i].spd > E[i].mark){
s[E[i].bel].erase(i);
E[i].bel += (E[i].spd - E[i].mark);
if (E[i].bel > Len) {
E[i].bel = 0;
tot--;
ans[i] = -Round;
continue;
}
s[E[i].bel].insert(i);
}
}
}
bool nooneyesman(){
bool flag = 1;
for(int i = 1 ; i <= head && flag ; i++){
if(E[i].bel) flag = 0;
}
return flag;
}
void Work() {
bool g = 1 , gg = 1;
while (tot) {
Round++;
summon() , locate() , attack();
check_damage() , move();
if(head == q && nooneyesman()) break;
}
}
void InTheEnd(){
cout << Round << " " << tot << "\n";
if(tot == 0) cout << "Xiao B is SB!\n";
else cout << "Victory!\n";
for(int i = 1 ; i <= q ; i++){
if(ans[i] > 0) cout << "Died in round" << " " << ans[i] << "\n";
else if(ans[i] < 0) cout << "Arrived your base in round" << " " << -ans[i] << "\n";
else cout << "The game has been over!\n";
}
}
signed main() {
Init();
Work();
InTheEnd();
return 0;
}