20210109~10 考试总结

都是雅礼2017集训的题

Day1

T1 决斗

有结论:存在至少一个位置 kk 满足对于任意的顺序都满足没有精灵从第 kk 个精灵旁走到第 k+1k+1 个精灵旁。

证明:定义 RiR_i 为一开始分配的侏儒对手编号小于或等于 ii 的精灵个数,并定义 Pi=RiiP_i =R_i-iPn=0P_n =0
永远成立。不妨设位置 mm 满足 PmP_m 是所有 PiP_i 里面最小的,可以证明永远不会有精灵从位置 mm 走到位置 m+1m+1。假设存在一个精灵从位置 mm 走到位置 m+1m+1,意味着存在一个序列 a,a+1,a+2,...,m1,ma, a+1,a+2,...,m-1,m 满足初始侏儒对手在这个区间的精灵数大于这些位置的数量。而初始侏儒对手在这个区间的精灵数减去这些位置的数量的差等于 PmPaP_m -P_a 。而由于 PmP_ m 是所有 PiP_i 中最小的,所有 PmPa>0P_m -P_a >0 永远无法成立,推出矛盾。

所以只需要从位置 kk 处把环断开,接下来就可以贪心地处理,每次从集合中选择力量值刚好大于当前侏儒的精灵,不存在则选择最小的。

需要一个支持插入一个数、删除一个数、查询集合内比 x 大的最小的数的数据结构,std::set 即可。

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
//#pragma GCC diagnostic error "-std=c++11"
//#pragma Gcc optimize(2)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdarg>
#include<typeinfo>
#include<vector>
#include<set>
#define debug(x) cerr << #x << " = " << x << endl
#define debugf(...) fprintf(stderr, __VA_ARGS__)
using namespace std;
template <class T> bool mvmax(T &a, T b) {return b > a ? (a = b, 1) : 0;}
template <class T> bool mvmin(T &a, T b) {return b < a ? (a = b, 1) : 0;}
void read(char *s) {scanf("%s", s);} void read(char &c) {scanf("%c", &c);}
template <class T> void read(T &a) {
a = 0; char c = getchar(); int f = 0;
while (!isdigit(c)) { f ^= c == '-', c = getchar(); }
while (isdigit(c)) { a = a * 10 + (c ^ 48), c = getchar(); }
a *= f ? -1 : 1;
}
struct Fastin {
template <class T> Fastin& operator >> (T &x) {read(x); return *this;}
} fastin;

const int MAXN = 1e5 + 10;

vector <int> input[MAXN];
set <int> buf;

int n;
int a[MAXN], p[MAXN], v[MAXN], cnt[MAXN];
int f[MAXN], g[MAXN], ming, m, ans;

int main() {
debugf("compiled on [%s]\n", __TIME__);
fastin >> n;
for (int i = 1; i <= n; i++) {
fastin >> a[i];
input[a[i]].push_back(i);
}
for (int i = 1; i <= n; i++) {
fastin >> p[i];
}
for (int i = 1; i <= n; i++) {
fastin >> v[i];
}
for (int i = 1; i <= n; i++) {
f[i] = f[i - 1] + input[i].size();
g[i] = f[i] - i;
if (mvmin(ming, g[i])) {
m = i;
}
}
for (int k = m + 1; k <= n + m; k++) {
int i = (k - 1) % n + 1;
for (int j = 0, siz = input[i].size(); j < siz; j++) {
buf.insert(v[input[i][j]]);
}
set <int> :: iterator it = buf.lower_bound(p[i]);
ans++;
if (it == buf.end()) {
it = buf.lower_bound(0);
ans--;
}
buf.erase(it);
}
printf("%d\n", ans);
return 0;
}

T2 数列

要计算题目所述的最长严格上升子序列,我们必须对于所有位置 XX 计算以 XX起始的最长上升子序列和以 XX 起始的最长下降子序列,以及各自所对应的方案数。用树状数组可以在 O(NlogN)O(NlogN) 的时间复杂度内计算上述的最长上升/下降子序列的长度以及方案数。
可以注意到,最终的答案是由原来的一个严格上升子序列和一个严格下降子序列共同组成的结果。假设 AA 是以 XX (包含 XX)起始的最长严格上升子序列的长度,BB 是以 XX 为(包含 XX)起始的最长下降子序列的长度,且方案数分别为numAnum_Anumbnum_b。可以得到用 XX 右边的数(包含 XX)组合出来的数列的最长严格上升子序列长度为 A+B1A+B-1,且方案数为 numA×numbnum_A\times num_b。最终要求的最长严格上升子序列的长度只需要在所有位置求出的最长上升子序列长度中取一个最大值,不妨设其为 RR。不难想到,对于每个位置计算出来的方案数,还需要乘 2(NR)2^{(N-R)}。因为在最长严格上升子序列中的 RR 个元素以外的元素可以任意放在左边或右边。
总的时间复杂度为 O(NlogN)O(NlogN)

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
//Author: ycp
//#pragma GCC diagnostic error "-std=c++11"
//#pragma Gcc optimize(2)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdarg>
#include<typeinfo>
#include<utility>
#define debug(x) cerr << #x << " = " << x << endl
//#define debugf(...) fprintf(stderr, __VA_ARGS__)
#define debugf(...)
using namespace std;
template <class T> bool mvmax(T &a, T b) {return b > a ? (a = b, 1) : 0;}
template <class T> bool mvmin(T &a, T b) {return b < a ? (a = b, 1) : 0;}
void read(char *s) {scanf("%s", s);} void read(char &c) {scanf("%c", &c);}
template <class T> void read(T &a) {
a = 0; char c = getchar(); int f = 0;
while (!isdigit(c)) { f ^= c == '-', c = getchar(); }
while (isdigit(c)) { a = a * 10 + (c ^ 48), c = getchar(); }
a *= f ? -1 : 1;
}
struct Fastin {
template <class T> Fastin& operator >> (T &x) {read(x); return *this;}
} fastin;

#define int long long

const int MAXN = 2e5 + 10;
const int mod = 1e9 + 7;

int pow2[MAXN], a[MAXN], b[MAXN], tot;
int n;

class Binary_indexed_tree {
int cnt[MAXN], val[MAXN];
public:
void insert(int pos, int upd_val, int upd_cnt) {
for (int i = pos; i <= tot + 5; i += (i & (-i))) {
if (val[i] < upd_val) {
val[i] = upd_val;
cnt[i] = upd_cnt;
} else if (val[i] == upd_val) {
(cnt[i] += upd_cnt) %= mod;
}
}
}
pair <int, int> query(int pos) {
int res_val = 0, res_cnt = 1;
for (int i = pos; i; i -= (i & (-i))) {
if (val[i] > res_val) {
res_val = val[i];
res_cnt = cnt[i];
} else if (val[i] == res_val) {
(res_cnt += cnt[i]) %= mod;
}
}
return make_pair(res_val, res_cnt);
}
} t1, t2;

int ans_len, ans_cnt;

signed main() {
debugf("compiled on [%s]\n", __TIME__);
fastin >> n;
for (int i = 1; i <= n; i++) {
fastin >> a[i];
b[i] = a[i];
}
sort(b + 1, b + n + 1);
tot = unique(b + 1, b + n + 1) - b - 1;
for (int i = 1; i <= n; i++) {
a[i] = lower_bound(b + 1, b + tot + 1, a[i]) - b;
}
pow2[0] = 1;
for (int i = 1; i <= n; i++) {
pow2[i] = (pow2[i - 1] << 1) % mod;
}
for (int i = n; i >= 1; i--) {
debugf("t1.query(%lld)\n", a[i] - 1);
pair <int, int> res1 = t1.query(a[i] - 1);
debugf("t2.query(%lld)\n", tot - a[i] + 1);
pair <int, int> res2 = t2.query(tot - a[i] + 1);
debugf("id = %lld, res1 = <%lld, %lld>, res2 = <%lld, %lld>\n", i, res1.first, res1.second, res2.first, res2.second);
res1.first += 1, res2.first += 1;
int len = res1.first + res2.first - 1;
int cnt = res1.second * res2.second % mod;
if (len == ans_len) {
(ans_cnt += pow2[n - len] * cnt % mod) %= mod;
} else if (len > ans_len) {
ans_cnt = pow2[n - len] * cnt % mod;
ans_len = len;
}
debugf("t1.insert(%lld, %lld, %lld)\n", a[i], res1.first, res1.second);
t1.insert(a[i], res1.first, res1.second);
debugf("t2.insert(%lld, %lld, %lld)\n", tot - a[i] + 2, res2.first, res2.second);
t2.insert(tot - a[i] + 2, res2.first, res2.second);
}
cout << ans_len << " "<< ans_cnt << endl;
return 0;
}

T3 拍苍蝇

问题可以被分解成两个较简单的问题:

  1. 对于给定的多边形,找到所有在多边形内部以及边上的整点。

  2. 对于每一个可能的多边形位置,判断是否有一个苍蝇所在位置等于多
    边形内部一个整点的位置。

对于第一个问题,我们可以枚举横坐标 xx ,逐一判断不同的边与 xx 的交点,细节较多(比方说线段端点直接在 xx 上的问题),可以看代码

对于第二个问题,用 std::bitset 即可维护。

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
//author: ycp | https://ycpedef.github.io
//#pragma GCC diagnostic error "-std=c++11"
//#pragma Gcc optimize(2)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdarg>
#include<bitset>
#include<vector>
#include<typeinfo>
#define debug(x) cerr << #x << " = " << x << endl
#define debugf(...) //fprintf(stderr, __VA_ARGS__)
using namespace std;
template <class T> bool cmax(T &a, T b) {return b > a ? (a = b, 1) : 0;}
template <class T> bool cmin(T &a, T b) {return b < a ? (a = b, 1) : 0;}
void read(char *s) {scanf("%s", s);} void read(char &c) {scanf("%c", &c);}
template <class T> void read(T &a) {
a = 0; char c = getchar(); int f = 0;
while (!isdigit(c)) { f ^= c == '-', c = getchar(); }
while (isdigit(c)) { a = a * 10 + (c ^ 48), c = getchar(); }
a *= f ? -1 : 1;
}
struct Fastin {
template <class T> Fastin& operator >> (T &x) {read(x); return *this;}
} fastin;

const int MAXN = 510;
//const int MAXN = 10;

struct Point {
int x, y;
} p[MAXN * 1000];

int n, m, k, q, maxx, maxy, minx, miny;
bitset <MAXN> graph[MAXN], fly[MAXN];
int ans[MAXN][MAXN];

int main() {
fastin >> n >> m >> q;
for (int i = 1, x, y; i <= q; i++) {
fastin >> x >> y;
fly[x][y] = 1;
}
fastin >> k;
maxx = 0x80808080, maxy = 0x80808080, minx = 0x3f3f3f3f, miny = 0x3f3f3f3f;
for (int i = 1; i <= k; i++) {
fastin >> p[i].x >> p[i].y;
cmax(maxx, p[i].x); cmin(minx, p[i].x);
cmax(maxy, p[i].y); cmin(miny, p[i].y);
}
maxx -= minx, maxy -= miny;
for (int i = 1; i <= k; i++) {
p[i].x -= minx, p[i].y -= miny;
graph[p[i].x][p[i].y] = 1;
//debugf("p[%d] = <%d, %d>\n", i, p[i].x, p[i].y);
}
//for (int i = 0; i <= maxx; i++) {
//debugf("graph[%d] = ", i);
//cerr << graph[i] << endl;
//}
//debugf("----------------\n");
for (int x = 0; x <= maxx; x++) {
vector <double> bpt;
for (int l = 1; l <= k; l++) {
int a = l, b = l % k + 1;
if (p[a].y > p[b].y) swap(a, b);
if ((p[a].x < x && p[b].x < x) || (p[a].x > x && p[b].x > x)) continue;
if ((p[a].x == x && p[b].x > x) || (p[a].x > x && p[b].x == x)) continue;
if (p[a].x == x && p[b].x == x) {
for (int y = p[a].y; y <= p[b].y; y++) {
graph[x][y] = 1;
}
continue;
}
double k = (double)(p[b].y - p[a].y) / (p[b].x - p[a].x);
bpt.push_back(p[a].y + k * (x - p[a].x));
}
sort(bpt.begin(), bpt.end());
int siz = bpt.size();
for (int y = 0, i = 0, flag = 0; y <= maxy; y++) {
while (i < siz && bpt[i] < y) i++, flag ^= 1;
if (flag || (i < siz && bpt[i] == y)) {
graph[x][y] = 1;
}
}
//debugf("graph[%d] = ", x);
//cerr << graph[x] << endl;
}
bitset <MAXN> cur;
for (int i = 0; i <= n - maxx; i++) {
for (int j = 0; j <= m - maxy; j++) {
ans[i][j] = 1;
for (int x = i; x <= i + maxx; x++) {
cur = graph[x - i] << j;
if ((cur & fly[x]).any()) {
ans[i][j] = 0;
break;
}
}
}
}
int res = 0;
for (int i = 0; i <= n - maxx; i++) {
for (int j = 0; j <= m - maxy; j++) {
res += ans[i][j];
}
}
printf("%d\n", res);
return 0;
}



Day2

T1 市场

考虑势能分析,令区间 [l,r][l,r] 的势能为 maxminmax-min ,则每次修改操作 1 最多改变 O(logn)O(\log n) 个区间的势能,操作 2 会使势能变为原来的 1/2\lfloor1/2\rfloor ,不需要修改几次就可以变为区间加法或者区间赋值运算。

复杂度 O(nlog2n)O(n\log^2n)

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
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
//author: ycp | https://ycpedef.github.io
//#pragma GCC diagnostic error "-std=c++11"
//#pragma Gcc optimize(2)
#include <algorithm>
#include <cmath>
#include <cstdarg>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <typeinfo>
#define debug(x) cerr << #x << " = " << x << endl
#define debugf(...) fprintf(stderr, __VA_ARGS__)
template <class T>
bool cmax(T& a, T b) { return b > a ? (a = b, 1) : 0; }
template <class T>
bool cmin(T& a, T b) { return b < a ? (a = b, 1) : 0; }
void read(char* s) { scanf("%s", s); }
void read(char& c) { scanf("%c", &c); }
template <class T>
void read(T& a) {
a = 0;
char c = getchar();
int f = 0;
while (!isdigit(c)) { f ^= c == '-', c = getchar(); }
while (isdigit(c)) { a = a * 10 + (c ^ 48), c = getchar(); }
a *= f ? -1 : 1;
}
struct Fastin {
template <class T>
Fastin& operator>>(T& x) {
read(x);
return *this;
}
} fastin;
using namespace std;

#define int long long
const int MAXN = 2e6 + 10;
const int inf = 0x3f3f3f3f3f3f3f3f;

struct Segment {
int val;
int minv;
int maxv;
int tag;
int fil;
Segment() {
val = 0;
minv = inf;
maxv = -inf;
tag = 0;
fil = 0;
}
} t[MAXN];

int a[MAXN], n, q;

#define ls ((p) << 1)
#define rs (((p) << 1) | 1)
#define mid ((l + r) >> 1)
#define val(p) t[p].val
#define tag(p) t[p].tag
#define minv(p) t[p].minv
#define maxv(p) t[p].maxv
#define fil(p) t[p].fil

void pushup(int p) {
val(p) = val(ls) + val(rs);
minv(p) = min(minv(ls), minv(rs));
maxv(p) = max(maxv(ls), maxv(rs));
}

void fill(int p, int l, int r, int v) {
//debugf("fill #%lld: [%lld, %lld] => %lld\n", p, l, r, v);
minv(p) = maxv(p) = fil(p) = v;
tag(p) = 0;
val(p) = (r - l + 1) * v;
}

inline void add(int p, int l, int r, int v) {
tag(p) += v;
val(p) += v * (r - l + 1);
minv(p) += v;
maxv(p) += v;
}

void pushdown(int p, int l, int r) {
if (fil(p) != inf) {
fill(ls, l, mid, fil(p));
fill(rs, mid + 1, r, fil(p));
fil(p) = inf;
}
if (tag(p)) {
//debugf("pushdown #%lld, @%lld, @%lld: fil = %lld, inf = %lld\n", p, l, r, fil(p), inf);
add(ls, l, mid, tag(p));
add(rs, mid + 1, r, tag(p));
tag(p) = 0;
//debugf("fuck pushdown #%lld, @%lld, @%lld: fil = %lld, inf = %lld\n", p, l, r, fil(p), inf);
}
}

void build_tree(int p, int l, int r) {
fil(p) = inf;
if (l == r) {
val(p) = a[l];
minv(p) = a[l];
maxv(p) = a[l];
} else {
build_tree(ls, l, mid);
build_tree(rs, mid + 1, r);
pushup(p);
}
}

void modify(int, int, int, int, int, int);

void div(int p, int l, int r, int x, int y, int c) {
//debugf("div in (#%lld, @%lld, @%lld)\n", p, l, r);
//if (l == r) {
// val(p) = (int)floor(1.0 * val(p) / c);
// minv(p) = val(p);
// maxv(p) = val(p);
// return;
//}
if (x <= l && r <= y) {
int delta_min = (int)(floor(1.0 * minv(p) / c)) - minv(p);
int delta_max = (int)(floor(1.0 * maxv(p) / c)) - maxv(p);
if (delta_min == delta_max) {
// debugf("shit c = %lld, %lld/%lld, %lld/%lld\n", c, minv(p), maxv(p), delta_min, delta_max);
modify(p, l, r, l, r, delta_min);
//debugf("delta $maxv = %lld, $minv = %lld, $delta = %lld\n", maxv(p), minv(p), delta_min);
return;
}
if (minv(p) + delta_min == maxv(p) + delta_max) {
//debugf("value skiped $val = %lld\n", minv(p) + delta_min);
// if (delta_min == 0);
// debugf("fuck c = %lld, %lld/%lld, %lld/%lld\n", c, minv(p), maxv(p), delta_min, delta_max);
fill(p, l, r, minv(p) + delta_min);
//debugf("FUCK fil(%lld)\n", fil(p));
return;
}
}
pushdown(p, l, r);
if (mid >= x)
div(ls, l, mid, x, y, c);
if (mid < y)
div(rs, mid + 1, r, x, y, c);
pushup(p);
}

void modify(int p, int l, int r, int x, int y, int c) {
if (x <= l && r <= y) {
add(p, l, r, c);
return;
}
pushdown(p, l, r);
if (mid >= x)
modify(ls, l, mid, x, y, c);
if (mid < y)
modify(rs, mid + 1, r, x, y, c);
pushup(p);
}

int query_sum(int p, int l, int r, int x, int y) {
pushdown(p, l, r);
if (x <= l && r <= y) {
return val(p);
}
int res = 0;
if (mid >= x)
res += query_sum(ls, l, mid, x, y);
if (mid < y)
res += query_sum(rs, mid + 1, r, x, y);
return res;
}

int query_min(int p, int l, int r, int x, int y) {
pushdown(p, l, r);
if (x <= l && r <= y) {
return minv(p);
}
int res = inf;
if (mid >= x)
cmin(res, query_min(ls, l, mid, x, y));
if (mid < y)
cmin(res, query_min(rs, mid + 1, r, x, y));
return res;
}

void print_tree(int p, int l, int r) {
debugf("#%lld\t[%lld, %lld]: ", p, l, r);
debugf("%lld / %lld, sum %lld, tag %lld, fil %s\n", minv(p), maxv(p), val(p), tag(p), fil(p) == inf ? "inf" : to_string(fil(p)).c_str());
//pushdown(p, l, r);
if (l != r)
print_tree(ls, l, mid), print_tree(rs, mid + 1, r);
}

void print_array(int p, int l, int r) {
if (l == r) return;
pushdown(p, l, r);
print_array(ls, l, mid);
print_array(rs, mid + 1, r);
}

#undef ls
#undef rs
#undef mid
#undef val
#undef tag
#undef minv

signed main() {
fastin >> n >> q;
for (int i = 1; i <= n; i++) {
fastin >> a[i];
}
build_tree(1, 1, n);
for (int i = 1, op, x, y, c; i <= q; i++) {
fastin >> op;
switch (op) {
case 1:
fastin >> x >> y >> c;
x++, y++;
debugf("---- modifing (%lld, %lld, +%lld) ----\n", x, y, c);
modify(1, 1, n, x, y, c);
print_tree(1, 1, n);
//print_array(1, 1, n); //debugf("\n");
// debugf("%lld\n", query_sum(1, 1, n, 655, 659));
break;
case 2:
fastin >> x >> y >> c;
x++, y++;
debugf("---- diving (%lld, %lld, %lld) ----\n", x, y, c);
div(1, 1, n, x, y, c);
print_tree(1, 1, n);
//print_array(1, 1, n), //debugf("\n");
// debugf("%lld\n", query_sum(1, 1, n, 655, 659));
break;
case 3:
fastin >> x >> y;
x++, y++;
debugf("---- min (%lld, %lld) ----\n", x, y);
printf("%lld\n", query_min(1, 1, n, x, y));
print_tree(1, 1, n);
break;
case 4:
fastin >> x >> y;
x++, y++;
debugf("---- sum (%lld, %lld) ----\n", x, y);
printf("%lld\n", query_sum(1, 1, n, x, y));
print_tree(1, 1, n);
break;
}
}
return 0;
}

T2 matrix

设初始状态含有白格子的列的数量为 cc ,设 kk 表示最少的操作次数使得某一行的所有格子变成黑色,那么在 kk 次操作之前,每一行都是不全为黑的,所以每次操作不会减少 cc ,而且为了步数最少, cc 肯定也不会增加,因此我们只要找到 kk ,那么答案就是 k+ck + c
考虑枚举全黑的那一行 rr, 设 xx 表示这一行白格子数量:

  1. 如果列 rr 有黑格子,那么最少次数为 xx ,只要把列 rr 有黑格子的那一行对每个白格子
    所在列进行操作即可。
  2. 如果没有,我们需要用一次操作来让这一列含有黑格子,所以要 x+1x + 1 步。

复杂度 O(n2)O(n^2)

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
//author: ycp | https://ycpedef.github.io
//#pragma GCC diagnostic error "-std=c++11"
//#pragma Gcc optimize(2)
#include <algorithm>
#include <cstdarg>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <typeinfo>
#define debug(x) cerr << #x << " = " << x << endl
#define debugf(...) //fprintf(stderr, __VA_ARGS__)
using namespace std;
template <class T>
bool cmax(T& a, T b) { return b > a ? (a = b, 1) : 0; }
template <class T>
bool cmin(T& a, T b) { return b < a ? (a = b, 1) : 0; }
void read(char* s) { scanf("%s", s); }
void read(char& c) { scanf("%c", &c); }
template <class T>
void read(T& a) {
a = 0;
char c = getchar();
int f = 0;
while (!isdigit(c)) { f ^= c == '-', c = getchar(); }
while (isdigit(c)) { a = a * 10 + (c ^ 48), c = getchar(); }
a *= f ? -1 : 1;
}
struct Fastin {
template <class T>
Fastin& operator>>(T& x) {
read(x);
return *this;
}
template <class T>
Fastin& operator>>(T* x) {
read(x);
return *this;
}
} fastin;

const int MAXN = 1010;
const int inf = 0x3f3f3f3f;
int n, c, _flag;
char s[MAXN][MAXN];

int main() {
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%s", s[i]);
int f = 1;
for (int j = 0, v; v = (s[i][j] == '#'), j < n; j++) _flag |= v, f &= v;
if (!f) c++;
}
if (!_flag) {
return puts("-1"), 0;
}
debugf("c = %d\n", c);
int ans = inf;
for (int i = 0; i < n; i++) {
int k = 0;
for (int j = 0; j < n; j++) k += (s[i][j] == '.');
debugf("i = %d, k = %d\n", i, k);
if (s[i][i] == '#')
cmin(ans, c + k);
else {
debugf("failed\n");
int flag = 0;
for (int j = 0; j < n; j++) {
if (s[j][i] == '#') {
flag = 1;
}
}
if (flag)
cmin(ans, c + k);
else
cmin(ans, c + k + 1);
}
}
// printf("%d\n", ans);
printf("%d\n", ans);
return 0;
}

T3 string

考虑当 k>mk > \sqrt m 时, qmq \le\sqrt m,因此每个询问我们对 i[a,b]i \in [a, b] 查询 w[li,ri]w[l_i , r_i ]ss 中出现次数, 用 SAM 可以做到单次 O(k+mlogn)O(k + m \cdot \log n)

考虑当 kmk \le \sqrt m时, k2m3/2\sum k^2 \le m^{3/2} ,因此 O(k2)O(k^2) 枚举 ww 的所有子串,对每个子串 w[l,r]w[l, r] 查询

出现次数以及询问次数,乘起来即可,单次 O(k2logn)O(k^2\log n)

总复杂度O(m\sqrt m \log n)。