2019牛客多校第三场

A. Graph Games

upsolved

\(n\)个点,\(m\)条边的图\((1<=n<=1e5,1<=m,q<=2e5)\)\(q\)次操作,操作有两种,一种是翻转区间内边的状态,第二种是询问两个点的邻接点集是否一致 直接判断点集肯定\(T\)飞了,给每个点随机一个权值,点集的权值就是全部异或起来,冲突概率很小

对线段分块,复杂度可以达到\(O(q\sqrt m)\)(代码里快读删了)

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
#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10, M = 2e5 + 10, sz = 510;

long long val[N], s[N], sum[sz][N];
int l[sz], r[sz], belong[M];
int u[M], v[M], lazy[sz];
int T, n, m, q, op, blk, num, x, y;

void build() {
blk = sqrt(m);
num = 1 + (m - 1) / blk;
for(int i = 1; i <= num; ++i) {
l[i] = (i - 1) * blk + 1;
r[i] = i * blk;
lazy[i] = 0;
for(int j = 1; j <= n; ++j)
sum[i][j] = 0;
}
r[num] = m;
for(int i = 1; i <= m; ++i)
belong[i] = (i - 1) / blk + 1;
for(int i = 1; i <= n; ++i)
s[i] = 0;
}

void update(int l, int r) {
if(belong[l] == belong[r]) {
for(int i = l; i <= r; ++i) {
s[u[i]] ^= val[v[i]];
s[v[i]] ^= val[u[i]];
}
return;
}
for(int i = l; belong[i] == belong[l]; ++i) {
s[u[i]] ^= val[v[i]];
s[v[i]] ^= val[u[i]];
}
for(int i = r; belong[i] == belong[r]; --i) {
s[u[i]] ^= val[v[i]];
s[v[i]] ^= val[u[i]];
}
for(int i = belong[l] + 1; i < belong[r]; ++i)
lazy[i] ^= 1;
}

int main() {
srand(time(NULL));
for(int i = 1; i <= 100000; ++i) val[i] = 1 + rand();
read(T);
while(T--) {
read(n); read(m);
build();
for(int i = 1; i <= m; ++i) {
read(u[i]); read(v[i]);
s[u[i]] ^= val[v[i]];
s[v[i]] ^= val[u[i]];
sum[belong[i]][u[i]] ^= val[v[i]];
sum[belong[i]][v[i]] ^= val[u[i]];
}
read(q);
while(q--) {
read(op); read(x); read(y);
if(op == 1)
update(x, y);
else {
long long a = s[x], b = s[y];
for(int i = 1; i <= num; ++i) if(lazy[i]) {
a ^= sum[i][x];
b ^= sum[i][y];
}
printf("%d", (int)(a == b));
}
}
puts("");
}
return 0;
}

B. Crazy Binary String

solved at 00:16

签到

D. Big Integer

upsolved 队友做的

F. Planting Trees

upsolved

\(O(n^3)\)单调队列,我是傻逼

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
#include <bits/stdc++.h>
using namespace std;

const int N = 505;

int d[N][N], n, m, T, ans, mx[N], mn[N];
int minn[N], maxx[N];
int h_mn, t_mn, h_mx, t_mx;

int main() {
scanf("%d", &T);
while(T--) {
ans = 0;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= n; ++j)
scanf("%d", &d[i][j]);
}
for(int up = 1; up <= n; ++up) {
memset(mx, 0, sizeof(mx));
memset(mn, 0x3f, sizeof(mn));
for(int down = up; down <= n; ++down) {
h_mn = t_mn = h_mx = t_mx = 0;
for(int k = 1, left = 0; k <= n; ++k) {
mx[k] = max(mx[k], d[down][k]);
mn[k] = min(mn[k], d[down][k]);
for(; h_mn != t_mn && mn[minn[t_mn - 1]] > mn[k]; ) t_mn--;
minn[t_mn++] = k;
for(; h_mx != t_mx && mx[maxx[t_mx - 1]] < mx[k]; ) t_mx--;
maxx[t_mx++] = k;
for(; h_mn != t_mn && h_mx != t_mx && mx[maxx[h_mx]] - mn[minn[h_mn]] > m;) {
if(maxx[h_mx] < minn[h_mn])
left = maxx[h_mx++];
else
left = minn[h_mn++];
}
ans = max(ans, (down - up + 1) * (k - left));
}
}
}
printf("%d\n", ans);
}
return 0;
}

G. Removing stones

upsolved

等价于询问有多少个区间的区间最大值小于等于区间和

题解说是找最大值然后枚举左右两边较短的那一边的端点然后二分查另一边的端点再分治,看了别人写了个暴力枚举不合法区间的然后跑的贼快,复杂度不太会算

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
#include <bits/stdc++.h>
using namespace std;

const int N = 3e5 + 10;

int a[N], T, n;
long long ans;

int main() {
scanf("%d", &T);
while(T--) {
ans = 0;
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
for(int i = 1; i <= n; ++i) {
int l = i, r = i;
long long sum = 0;
while(l > 1 && sum + a[l - 1] < a[i]) l--, sum += a[l];
while(r < n && sum + a[r + 1] < a[i]) r++, sum += a[r];
ans += r - i + 1;
for(int j = l; j < i; ++j) {
sum -= a[j];
while(r < n && sum + a[r + 1] < a[i]) r++, sum += a[r];
ans += r - i + 1;
}
}
printf("%lld\n", 1LL * n * (n + 1) / 2- ans);
}
return 0;
}

H. Magic Line

solved at 00:36

要求用一条直线把平面上的点分成两部分,两部分个数相同且不能有点在线上

注意到点的坐标范围只有\(1000\)而你最终输出的线的坐标可以到\(1e9\)

一定可以用一条近似竖直线的线

把点分开

I. Median

upsolved

给定原序列的中位数序列(长度为\(n-2\)),构造一个原序列

J. LRU management

solved at 04:59(+22)

队友做的,他手写了一个链表。。。

用几个map维护就好了,利用map的迭代器可以自增或是自减的性质就好了

那个string可以看成一个11进制数(区分前置零)

我在贴上来的时候把快读删掉了,就是read那个函数的实现,用的是fread

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
#include <bits/stdc++.h>
using namespace std;

const int N = 5e5 + 10;
int T, q, m, op, v;
long long ss;
char s[12];
map<int, long long> ptos;
map<long long, int> stop, stov;

long long convert(char s[]) {
long long val = 0;
for(int i = 0; s[i]; ++i) {
val = val * 11 + s[i] - '0' + 1;
}
return val;
}

int main() {
read(T);
while(T--) {
ptos.clear(); stop.clear(); stov.clear();
read(q); read(m);
ptos[100000] = 1e18;
stop[1000000000000000000LL] = 100000;
stov[1000000000000000000LL] = 100;
while(q--) {
read(op); read(s); read(v);
ss = convert(s);
if(op == 0) {
if(stov.count(ss)) {
printf("%d\n", stov[ss]);
int fi = ptos.begin()->first;
int pos = stop[ss];
ptos.erase(ptos.find(pos));
stop[ss] = fi - 1;
ptos[fi - 1] = ss;
}
else {
stov[ss] = v;
int fi = ptos.begin()->first;
stop[ss] = fi - 1;
ptos[fi - 1] = ss;
if(ptos.size() > m) {
long long es = ptos.rbegin()->second;
ptos.erase(--ptos.end());
stop.erase(stop.find(es));
stov.erase(stov.find(es));
}
printf("%d\n", v);
}
}
else {
if(!stov.count(ss)) {
puts("Invalid");
continue;
}
int pos = stop[ss];
if(v == 0) {
printf("%d\n", stov[ss]);
}
else {
map<int, long long>::iterator it = ptos.find(pos);
if(v == 1) {
if(it == ptos.begin()) {
puts("Invalid");
continue;
}
it--;
if(stov[it->second] == 100) {
puts("Invalid");
continue;
}
printf("%d\n", stov[it->second]);
}
else {
if(++it == ptos.end()) {
puts("Invalid");
continue;
}
if(stov[it->second] == 100) {
puts("Invalid");
continue;
}
printf("%d\n", stov[it->second]);
}
}
}
}
}
return 0;
}

用map维护string(实际上是数)到list<int>::iterator的映射似乎写起来更简单