SCUACM 2016集训 7.13【16/18】

集训期间完成13道,补题3道。
比赛链接:http://vjudge.net/contest/121106#overview

A - SPF(割点)

题意:

求无向图割点,以及除去割点后的连通子图的数量

分析:

tarjan求割点

代码:

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
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <set>
using namespace std;
typedef long long ll;
#define sa(n) scanf("%d", &(n))
#define sal(n) scanf("%I64d", &(n))
const int maxn = 1e3 + 5, oo = 0x3f3f3f3f, mod = 1e9 + 7;
int head[maxn], dfn[maxn], low[maxn];
struct EDGE{int to; int next;};
EDGE edge[maxn * 2];
int tot = 0, num, root;
int cnt[maxn];
void init()
{
tot = 0;
num = 1;
memset(head, -1, sizeof(head));
memset(dfn, 0, sizeof(dfn));
memset(cnt, 0, sizeof(cnt));
memset(low, 0, sizeof(low));
}
void addedge(int from, int to)
{
edge[tot].to = to;
edge[tot].next = head[from];
head[from] = tot++;
edge[tot].to = from;
edge[tot].next = head[to];
head[to] = tot++;
}
void dfs(int u)
{
dfn[u] = low[u] = num++;
for(int i = head[u]; i != -1; i = edge[i].next){
int v = edge[i].to;
if(!dfn[v]){
dfs(v);
low[u] = min(low[u], low[v]);
if(u == root) {cnt[u]++; continue;}
if(low[v] >= dfn[u]) cnt[u]++;
}else{
low[u] = min(low[u], dfn[v]);
}
}
}
set<int>s;
set<int>::iterator si;
int main(void)
{
int u, v;
int tt = 0;
while(scanf("%d", &u) && u) {
init();
root = u;
s.clear();
scanf("%d", &v);
s.insert(u);s.insert(v);
addedge(u, v);
while(scanf("%d", &u) && u) {
scanf("%d", &v);
addedge(u, v);
s.insert(u);s.insert(v);
}
dfs(root);
bool flag = false;
if(tt) printf("\n");
printf("Network #%d\n", ++tt);
for (si = s.begin(); si != s.end(); si++) {
if(*si == root) cnt[*si]--;
if(cnt[*si] > 0){
printf(" SPF node %d leaves %d subnets\n", *si, cnt[*si] + 1);
flag = true;
}
}
if(!flag) {
printf(" No SPF nodes\n");
}
}
return 0;
}

B - Boonie and Clyde(割点)

题意:

给定无向图,去掉两个点,使得图不再连通,问有多少种选择方式?

分析:

枚举每个点,求剩下的图的割点个数。如果枚举的点为割点,则在剩下的图中任选一点都可以,如果非割点,即去掉这个点,剩下的图连通性不变,则只能再选剩下图的割点。这样复杂度为$O(n \times m)$,可以一试。
但是情况并没有这么简单= =
如果枚举的点是割点,剩下的图被分成若干部分,这些部分之间的点不再连通。最后我们还要在剩下的部分中再随便去掉一点,但是如果仅有两部分且其中一部分只有一个点,那么去掉这个点之后相当于只剩下了一部分,显然此时是这个选择是无解的QAQ
所以我们必须对去掉割点后,剩下图被分为两部分,且至少有一部分仅有一个点剩下的情况进行特判。

代码:

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
/*************************************************************************
> File Name: 3671.cpp
> Author: jiangyuzhu
> Mail: 834138558@qq.com
> Created Time: 2016/9/13 23:14:54
************************************************************************/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<algorithm>
using namespace std;
const int maxn = 1e3 + 5, maxm = 1e4 + 5;
int dfnum;
int low[maxn], dfn[maxn];
struct EDGE{
int to, next;
bool flag;
}edge[maxm << 1];
int head[maxn];
int tot;
int cnt;
bool vis[maxn];
void init()
{
memset(vis, 0, sizeof(vis));
memset(low, 0, sizeof(low));
memset(dfn, 0, sizeof(dfn));
dfnum = 1;//1开始
cnt = 0;
}
void addedge(int from, int to)
{
edge[tot].to = to;
edge[tot].next = head[from];
head[from] = tot++;
}
int num;
void dfs(int u, int pa, int k)
{
int child = 0;
dfn[u] = low[u] = dfnum++;
num++;
for(int i = head[u]; i != -1; i = edge[i].next){
int v = edge[i].to;
if(v == k || v == pa) continue;
if(!dfn[v]){
child++;
dfs(v, u, k);
low[u] = min(low[u], low[v]);
if(pa == -1 && child > 1){
if(!vis[u]) cnt++;
vis[u] = true;
}
if(pa != -1 && low[v] >= dfn[u]){
if(!vis[u]) cnt++;
vis[u] = true;
}
}else{
low[u] = min(low[u], dfn[v]);
}
}
}
int main (void)
{
int n, m;
int kas = 1;
while(~scanf("%d%d", &n, &m) && (n + m)){
int u, v;
memset(head, -1, sizeof(head));
tot = 0;
for(int i = 0; i < m; ++i){
scanf("%d%d", &u, &v);
addedge(u, v);
addedge(v, u);
}
int ans = 0;
for(int i = 1; i <= n; ++i){
init();
int cntt = 0, t = 0;
for(int j = 1; j <= n; j++){
if(j == i || dfn[j]) continue;
cntt++;
num = 0;
dfs(j, -1, i);
if(num == 1) t++;
}
if(cntt == 1) ans += cnt;
else if(cntt == 2 && t == 1) ans += n - 2;
else if(cntt == 2 && t == 0) ans += n - 1;
else if(cntt > 2) ans += n - 1;
}
printf("Case %d: %d\n", kas++, ans / 2);
}
return 0;
}

D - Matrix Decompressing【网络流】

题意:

给定各行各列的和,求出一个满足条件的矩阵。

分析:

最初没想到,听陈鹏说是网络流突然觉得好有道理,这样想来好像也是很裸的样子。各行各列相连,从超级源点到每行连线,流量为各行的和,从各列向超级汇点连线,流量为各列的和。这样跑一下网络流再往回找一各边的流量即为答案。注意此题有流量至少为1的下界, 所以初始就为每条边安排1的流量,那么行列之间边的容量为19,从源点流入和汇点流出的值要进行相应的减少。跑完网络流最后输出答案再加上一即可。

代码:

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
/*************************************************************************
> File Name: G.cpp
> Author: jiangyuzhu
> Mail: 834138558@qq.com
> Created Time: 2016/7/16 23:01:48
************************************************************************/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<algorithm>
using namespace std;
struct edge{int to, cap, rev, flow;};
const int maxn = 100 + 5, maxm = maxn * maxn + 5, INF = 0x3f3f3f3f;
int d[maxm], iter[maxm];
int s, t;
vector<edge>G[maxm * 2];
void init()
{
for(int i = 0; i < maxn; i++) G[i].clear();
}
void addedge(int from, int to, int cap)
{
G[from].push_back((edge){to, cap, G[to].size(), 0});
G[to].push_back((edge){from, 0, G[from].size()-1, 0});
}
void bfs()
{
memset(d, -1, sizeof(d));
queue<int>q;
d[s] = 0;
q.push(s);
while(!q.empty()){
int v = q.front();q.pop();
for(int i = 0; i < G[v].size(); i++){
edge &e = G[v][i];
if(e.cap > e.flow && d[e.to] == -1){
d[e.to] = d[v] + 1;
q.push(e.to);
}
}
}
}
int dfs(int v, int f)
{
if(v == t) return f;
for(int &i = iter[v]; i < G[v].size(); i++){
edge &e = G[v][i];
if(e.cap > e.flow && d[v] == d[e.to] - 1){
int tf = dfs(e.to, min(f, e.cap - e.flow));
if(tf > 0){
e.flow += tf;
G[e.to][e.rev].flow -= tf;
return tf;
}
}
}
return 0;
}
int max_flow()
{
int flow = 0;
for(;;){
bfs();
if(d[t]<0) return flow;
memset(iter, 0, sizeof(iter));
int f;
while((f = dfs(s, INF))>0){
flow += f;
}
}
}
int a[maxn], b[maxn];
int res[maxn][maxn];
int main (void)
{
int T;scanf("%d", &T);
for(int kas = 1; kas <= T; kas++){
int n, m;
init();
scanf("%d%d", &n, &m);
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
int tmp1, tmp2 = 0;
for(int i = 1; i <= n; i++){
scanf("%d", &tmp1);
a[i] = tmp1 - tmp2;
tmp2 = tmp1;
}
tmp2 = 0;
for(int i = 1; i <= m; i++){
scanf("%d", &tmp1);
b[i] = tmp1 - tmp2;
tmp2 = tmp1;
}
s = 0, t = n + m + 1;
for(int i = 1; i <= n; i++){
addedge(s, i, a[i] - m);
}
for(int i = 1; i <= m; i++){
addedge(i + n, t, b[i] - n);
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
addedge(i, j + n, 19);
}
}
int ans = max_flow();
memset(res, 0, sizeof(res));
for(int i = 1; i <= n; i++){
for(int j = 0; j < G[i].size(); j++){
res[i][G[i][j].to - n] = G[i][j].flow;
}
}
printf("Matrix %d\n", kas);
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
printf("%d%c", res[i][j] + 1, j == m?'\n':' ');
}
}
puts("");
}
return 0;
}

E - Pie(二分)

题意:

若干蛋糕分给若干人,要求每个人分到的蛋糕是完整的且大小相同,问最大是多少?

分析:

每个人得到的蛋糕越大,得到蛋糕的人数就越少,满足一定单调性,明显二分。

代码:

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
#include<iostream>
#include<map>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn = 1e4 + 5;
const double eps = 1e-8;
int ra[maxn];
int f, n;
const double pi = acos(-1);
bool judge(double mid)
{
int cnt = 0;
for(int i = n - 1; i >= 0; i--){
cnt +=(int)(ra[i] * 1.0 / mid);
if(cnt >= f) return true;
}
return cnt >= f;
}
int main (void)
{
int T;scanf("%d", &T);
while(T--){
scanf("%d%d", &n, &f);
f++;
for(int i = 0; i < n; i++){
scanf("%d", &ra[i]);
ra[i] = ra[i] * ra[i];
}
sort(ra, ra + n);
double l = 0, r = ra[n - 1];
while(l + eps < r){
double mid = (l + r) / 2.0;
if(judge(mid)) l = mid;
else r = mid;
}
printf("%.4f\n", l *1.0 * pi);
}
return 0;
}

F - K Best(二分)

题意:

给定$v和w$序列,从中选择$k$个数,使得$ \sum_{j=1}^k v_{i_j}\over \sum_{j=1} w_{i_J}$最大。

分析:

最大化平均值问题,我们采用二分答案的方法。
设$s ={ \sum_{j=1}^k v_i\over \sum_{j=1} w_i}$,二分$s$,移项整理得$ \sum_{j=1}^k w_i = s * \sum_{j=1}^k v_i$,每次判断能否得到平均值大于等于$s$的$k$个序列的时候,对$v_i - w_i$从大到小排序,贪心的选择最大的$k$个看是否大于等于$0$即可。有点巧妙。

代码:

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
#include<iostream>
#include<map>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn = 1e5 + 5;
const double eps = 1e-8;
double s[maxn];
struct Node{
int v, w;
int id; double s;
bool operator < (const Node &a) const{
return s > a.s;
}
};
Node node[maxn];
int n, k;
bool judge(double mid)
{
for(int i = 0; i < n; i++){
node[i].s = node[i].v - mid *1.0 * node[i].w;
}
sort(node, node + n);
double ans = 0;
for(int i = 0; i < k; i++){
ans += node[i].s;
}
return ans >= 0;
}
int main (void)
{
scanf("%d%d", &n, &k);
int v, w;
double t = 0.0;
for(int i = 0; i < n; i++){
scanf("%d%d", &v, &w);
node[i] = (Node){v, w, i + 1};
t = max(t, v * 1.0 / (w * 1.0));
}
double l = 0, r = t;
for(int i = 0; i < 100; i++){
double mid = (l + r) / 2.0;
if(judge(mid)) l = mid;
else r = mid;
}
for(int i = 0; i < n; i++){
node[i].s = node[i].v - l *1.0 * node[i].w;
}
sort(node, node + n);
for(int i = 0; i < k; i++){
printf("%d%c", node[i].id, i == n - 1?'\n':' ');
}
return 0;
}

G - Median(二分)

题意:

给定序列,求序列所有元素差值的绝对值的中位数。

分析:

利用中位数的位置特点,枚举中位数,找大于等于这个中位数的差值的个数,肯定是中位数越大,个数就越小,利用这个单调性,二分中位数,然后对于每个元素看加上这个差值之后序列中大于这个数的元素个数即可。(这里的处理方法很经典),此处用$lower_ bound$实现。

代码:

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
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
#define sal(a) scanf("%I64d", &(a))
const int maxn = 1e5 + 5, oo = 0x3f3f3f3f;
ll a[maxn];
int n;
bool judge(ll mid)
{
ll cnt = 0;
for(int i = 0; i < n; i++){
int t = n - (lower_bound(a, a + n, a[i] + mid) - a);
//cout<<a[i]<<' '<<mid<<' '<<cnt<<endl;
cnt += t;
if(cnt > (n * 1ll * (n - 1)) / 4) return true;
}
if(cnt > (n * 1ll * (n - 1)) / 4) return true;
return false;
}
int main (void)
{
while(~scanf("%d", &n)){
for(int i = 0; i < n; i++)
sal(a[i]);
sort(a, a + n);
ll l = 0, r = a[n - 1] - a[0] + 1;
while(l < r - 1){
ll mid = (l + r) / 2;
if(judge(mid)) l = mid;
else r = mid;
}
printf("%I64d\n", l);
}
return 0;
}

H - Showstopper(二分)

题意:

一堆偶数中有一个奇数,求这个奇数。

分析:

这道题很无聊啊,不给数据范围,输入又那么恶心。
思想很基本,利用数奇偶的特点及性质即,偶数+奇数=奇数
我们可以把所有数按一定顺序求个前缀和,之前一直是偶数,一旦遇到奇数,加上奇数,后面所有得到的前缀和就都为奇数。利用这个单调性进行二分。
利用$gets$从输入流读取,使用$sscanf$

gets()函数从流中读取字符串,直到出现换行符或读到文件尾为止。最后加上NULL作为字符串结束。所读取的字符串暂存在给定的参数string中。

读入后,处理这种输入有两种方法很巧妙:

  1. `while(gets(s)){
    sscanf(s, "%lld%lld%lld", &t, &y[id], &z[id]);}`读入换行符视为啥也没读入
    
  2. 1
    2
    3
    4
    5
    6
    7
    8
    9
    bool read()
    {
    int eof = 0;id = 0;
    while((eof = (gets(s) != NULL)) && strlen(s) > 2){
    sscanf(s, "%lld%lld%lld", &x[id], &y[id], &z[id]);
    id++;
    }
    return eof||id;
    }

代码:

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
#include<iostream>
#include<map>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
const ll oo = 1ll << 32;
ll x[maxn], y[maxn], z[maxn];
char s[maxn];
int id;
ll gao(ll mid)
{
ll cnt = 0;
for(int i = 0; i < id; i++){
if(x[i] <= mid){
ll tmp = min(mid, y[i]);
cnt += (tmp - x[i]) / z[i] + 1;
}
}
return cnt;
}
void solve()
{
ll l = -1, r = oo;
while(l < r - 1){
ll mid = (l + r) / 2;
if(gao(mid) % 2) r = mid;
else l = mid;
}
if(r == oo) puts("no corruption");
else{
printf("%lld ", r);
ll cnt = 0;
for(int i = 0; i < id; i++){
if(x[i] <= r && r <= y[i]){
if((r - x[i]) % z[i] == 0) cnt++;
}
}
printf("%lld\n", cnt);
}
}
int main (void)
{
ll t = 0;
id = 0;
while(gets(s)){
sscanf(s, "%lld%lld%lld", &t, &y[id], &z[id]);
if(t == 0){
if(id) solve();
t = 0;
id = 0;
}else{
x[id++] = t;
t = 0;
}
}
if(id) solve();
return 0;
}

I - Censored!(AC自动机+DP+大数)

题意:

给定若干字符串,求不含这些字符串的长度固定的字符串种类、

分析:

比较模板的一道题,AC自动机经典问题,题目数目会爆$long long$,套个大数模板即可【大数模板蒯自jibancanyang。

代码:

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
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
/*************************************************************************
> File Name: I.cpp
> Author: jiangyuzhu
> Mail: 834138558@qq.com
> Created Time: 2016/7/15 21:29:21
************************************************************************/
#include<cstdio>
#include<iostream>
#include<queue>
#include<map>
#include<cstring>
using namespace std;
#define pr(x) cout << #x << ": " << x << " "
#define pl(x) cout << #x << ": " << x << endl;
#define sa(x) scanf("%d",&(x))
#define sal(x) scanf("%I64d",&(x))
#define mdzz cout<<"mdzz"<<endl;
const int maxn = 50 + 5, maxm = 5e2 + 5, oo = 0x3f3f3f3f, mod = 1e9 + 7;
typedef long long ll;
char s[maxn];
map<char, int>id;
#define MAXN 9999
#define MAXSIZE 10
#define DLEN 4
class BigNum
{
private:
int a[40]; //可以控制大数的位数
int len; //大数长度
public:
BigNum(){ len = 1;memset(a,0,sizeof(a)); } //构造函数
BigNum(const int); //将一个int类型的变量转化为大数
BigNum(const char*); //将一个字符串类型的变量转化为大数
BigNum(const BigNum &); //拷贝构造函数
BigNum &operator=(const BigNum &); //重载赋值运算符,大数之间进行赋值运算
friend istream& operator>>(istream&, BigNum&); //重载输入运算符
friend ostream& operator<<(ostream&, BigNum&); //重载输出运算符
BigNum operator+(const BigNum &) const; //重载加法运算符,两个大数之间的相加运算
BigNum operator-(const BigNum &) const; //重载减法运算符,两个大数之间的相减运算
BigNum operator*(const BigNum &) const; //重载乘法运算符,两个大数之间的相乘运算
BigNum operator/(const int &) const; //重载除法运算符,大数对一个整数进行相除运算
BigNum operator^(const int &) const; //大数的n次方运算
int operator%(const int &) const; //大数对一个int类型的变量进行取模运算
bool operator>(const BigNum & T)const; //大数和另一个大数的大小比较
bool operator>(const int & t)const; //大数和一个int类型的变量的大小比较
void print(); //输出大数
};
BigNum::BigNum(const int b) //将一个int类型的变量转化为大数
{
int c,d = b;
len = 0;
memset(a,0,sizeof(a));
while(d > MAXN)
{
c = d - (d / (MAXN + 1)) * (MAXN + 1);
d = d / (MAXN + 1);
a[len++] = c;
}
a[len++] = d;
}
BigNum::BigNum(const char*s) //将一个字符串类型的变量转化为大数
{
int t,k,index,l,i;
memset(a,0,sizeof(a));
l=strlen(s);
len=l/DLEN;
if(l%DLEN)
len++;
index=0;
for(i=l-1;i>=0;i-=DLEN)
{
t=0;
k=i-DLEN+1;
if(k<0)
k=0;
for(int j=k;j<=i;j++)
t=t*10+s[j]-'0';
a[index++]=t;
}
}
BigNum::BigNum(const BigNum & T) : len(T.len) //拷贝构造函数
{
int i;
memset(a,0,sizeof(a));
for(i = 0 ; i < len ; i++)
a[i] = T.a[i];
}
BigNum & BigNum::operator=(const BigNum & n) //重载赋值运算符,大数之间进行赋值运算
{
int i;
len = n.len;
memset(a,0,sizeof(a));
for(i = 0 ; i < len ; i++)
a[i] = n.a[i];
return *this;
}
istream& operator>>(istream & in, BigNum & b) //重载输入运算符
{
char ch[MAXSIZE*4];
int i = -1;
in>>ch;
int l=strlen(ch);
int count=0,sum=0;
for(i=l-1;i>=0;)
{
sum = 0;
int t=1;
for(int j=0;j<4&&i>=0;j++,i--,t*=10)
{
sum+=(ch[i]-'0')*t;
}
b.a[count]=sum;
count++;
}
b.len =count++;
return in;
}
ostream& operator<<(ostream& out, BigNum& b) //重载输出运算符
{
int i;
cout << b.a[b.len - 1];
for(i = b.len - 2 ; i >= 0 ; i--)
{
cout.width(DLEN);
cout.fill('0');
cout << b.a[i];
}
return out;
}
BigNum BigNum::operator+(const BigNum & T) const //两个大数之间的相加运算
{
BigNum t(*this);
int i,big; //位数
big = T.len > len ? T.len : len;
for(i = 0 ; i < big ; i++)
{
t.a[i] +=T.a[i];
if(t.a[i] > MAXN)
{
t.a[i + 1]++;
t.a[i] -=MAXN+1;
}
}
if(t.a[big] != 0)
t.len = big + 1;
else
t.len = big;
return t;
}
BigNum BigNum::operator-(const BigNum & T) const //两个大数之间的相减运算
{
int i,j,big;
bool flag;
BigNum t1,t2;
if(*this>T)
{
t1=*this;
t2=T;
flag=0;
}
else
{
t1=T;
t2=*this;
flag=1;
}
big=t1.len;
for(i = 0 ; i < big ; i++)
{
if(t1.a[i] < t2.a[i])
{
j = i + 1;
while(t1.a[j] == 0)
j++;
t1.a[j--]--;
while(j > i)
t1.a[j--] += MAXN;
t1.a[i] += MAXN + 1 - t2.a[i];
}
else
t1.a[i] -= t2.a[i];
}
t1.len = big;
while(t1.a[len - 1] == 0 && t1.len > 1)
{
t1.len--;
big--;
}
if(flag)
t1.a[big-1]=0-t1.a[big-1];
return t1;
}
BigNum BigNum::operator*(const BigNum & T) const //两个大数之间的相乘运算
{
BigNum ret;
int i,j,up;
int temp,temp1;
for(i = 0 ; i < len ; i++)
{
up = 0;
for(j = 0 ; j < T.len ; j++)
{
temp = a[i] * T.a[j] + ret.a[i + j] + up;
if(temp > MAXN)
{
temp1 = temp - temp / (MAXN + 1) * (MAXN + 1);
up = temp / (MAXN + 1);
ret.a[i + j] = temp1;
}
else
{
up = 0;
ret.a[i + j] = temp;
}
}
if(up != 0)
ret.a[i + j] = up;
}
ret.len = i + j;
while(ret.a[ret.len - 1] == 0 && ret.len > 1)
ret.len--;
return ret;
}
BigNum BigNum::operator/(const int & b) const //大数对一个整数进行相除运算
{
BigNum ret;
int i,down = 0;
for(i = len - 1 ; i >= 0 ; i--)
{
ret.a[i] = (a[i] + down * (MAXN + 1)) / b;
down = a[i] + down * (MAXN + 1) - ret.a[i] * b;
}
ret.len = len;
while(ret.a[ret.len - 1] == 0 && ret.len > 1)
ret.len--;
return ret;
}
int BigNum::operator %(const int & b) const //大数对一个int类型的变量进行取模运算
{
int i,d=0;
for (i = len-1; i>=0; i--)
{
d = ((d * (MAXN+1))% b + a[i])% b;
}
return d;
}
BigNum BigNum::operator^(const int & n) const //大数的n次方运算
{
BigNum t,ret(1);
int i;
if(n<0)
exit(-1);
if(n==0)
return 1;
if(n==1)
return *this;
int m=n;
while(m>1)
{
t=*this;
for( i=1;i<<1<=m;i<<=1)
{
t=t*t;
}
m-=i;
ret=ret*t;
if(m==1)
ret=ret*(*this);
}
return ret;
}
bool BigNum::operator>(const BigNum & T) const //大数和另一个大数的大小比较
{
int ln;
if(len > T.len)
return true;
else if(len == T.len)
{
ln = len - 1;
while(a[ln] == T.a[ln] && ln >= 0)
ln--;
if(ln >= 0 && a[ln] > T.a[ln])
return true;
else
return false;
}
else
return false;
}
bool BigNum::operator >(const int & t) const //大数和一个int类型的变量的大小比较
{
BigNum b(t);
return *this>b;
}
void BigNum::print() //输出大数
{
int i;
cout << a[len - 1];
for(i = len - 2 ; i >= 0 ; i--)
{
cout.width(DLEN);
cout.fill('0');
cout << a[i];
}
cout << endl;
}
BigNum dp[maxn][maxm];
struct trie{
int fail, next[maxn];
int tag;
}ans[maxm];
int N, M;
int init(int x)
{
for(int i = 0; i < N; i++) ans[x].next[i] = 0;
ans[x].fail = 0;
ans[x].tag = 0;
return x;
}
int tot;
void insert(char *s)
{
int now = 0;
for(; *s; s++){
int t = id[*s];
if(ans[now].next[t] == 0) ans[now].next[t] = init(++tot);
now = ans[now].next[t];
}
ans[now].tag = 1;
}
void ACbfs()
{
queue<int>q;
for(int i = 0; i < N; i++){
if(ans[0].next[i]) q.push(ans[0].next[i]);
}
while(!q.empty()){
int u = q.front();q.pop();
ans[u].tag =ans[u].tag | ans[ans[u].fail].tag;
for(int i = 0; i < N; i++){
int &v = ans[u].next[i];
if(v){
q.push(v);
ans[v].fail = ans[ans[u].fail].next[i];
}else{
v = ans[ans[u].fail].next[i];
}
}
}
}
char t[maxn];
int main (void)
{
int p;
scanf("%d%d%d", &N, &M, &p);
tot = 0;
init(0);
scanf("%s", t);
for(int i = 0; i < N; i++){
id[t[i]] = i;
}
for(int i = 0; i < p; i++){
scanf("%s", s);
insert(s);
}
ACbfs();
for(int i = 0; i < M; i++){
for(int j = 0; j <= tot; j++){
dp[i][j] = 0;
}
}
dp[0][0] = 1;
for(int i = 0; i < M; i++){
for(int j = 0; j <= tot; j++){
for(int z = 0; z < N; z++){
int v = ans[j].next[z];
if(ans[v].tag == 0){
dp[i + 1][v] = dp[i + 1][v] + dp[i][j];
}
}
}
}
BigNum anss = 0;
for(int i = 0; i <= tot; i++){
anss = anss + dp[M][i];
}
anss.print();
return 0;
}

K - Palindromes

题意:

判断一个字符串是回文的还是镜面的。

分析:

超级水题,map的时候别写错了就行。

代码:

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
#include<iostream>
#include<map>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn = 20 + 5;
char s[maxn];
map<char, char>F;
void init()
{
F['A'] = 'A';F['Z'] = '5';
F['E'] = '3';F['1'] = '1';
F['H'] = 'H';F['2'] = 'S';
F['I'] = 'I';F['3'] = 'E';
F['J'] = 'L';F['5'] = 'Z';
F['L'] = 'J';F['8'] = '8';
F['M'] = 'M';F['O'] = 'O';
F['S'] = '2';F['T'] = 'T';
F['U'] = 'U';F['V'] = 'V';
F['W'] = 'W';F['X'] = 'X';
F['Y'] = 'Y';
}
int main (void)
{
init();
bool flag1, flag2;
while(~scanf("%s", s)){
int len = strlen(s);
flag1 = flag2 = true;
for(int i = 0, j = len - 1; j >= i; i++, j--){
if(flag1 && F[s[i]] != s[j]) flag1 = false;
if(flag2 && s[i] != s[j]) flag2 = false;
if(!flag1 && !flag2) break;
}
printf("%s", s);
if(!flag1 && !flag2) puts(" -- is not a palindrome.");
else if(!flag1 && flag2) puts(" -- is a regular palindrome.");
else if(!flag2 && flag1) puts(" -- is a mirrored string.");
else puts(" -- is a mirrored palindrome.");
cout<<endl;
}
return 0;
}

L - Tiling Dominoes(轮廓线dp)

题意:

给定$n\times m \le 100$的矩阵,让你用$1\times 2$的方块填满,可以横着放也可以竖着放,问你有多少种放法。

分析:

通过这题学习轮廓线dp。大白书上讲解的轮廓线dp很清晰,注意$memset$会超时,数组开到$10+1$即可,或者直接$for$遍历一遍。

代码:

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
/*************************************************************************
> File Name: L.cpp
> Author: jiangyuzhu
> Mail: 834138558@qq.com
> Created Time: 2016/7/14 23:55:22
************************************************************************/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 10 + 1, oo = 0x3f3f3f3f;
ll dp[2][1 << maxn];
int main (void)
{
int n, m;
while(scanf("%d%d", &n, &m) == 2){
if(n < m) swap(n, m);
memset(dp, 0, sizeof(dp));
int now = 0;
dp[now][(1 << m) - 1] = 1;
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
now ^= 1;
memset(dp[now], 0, sizeof(dp[now]));
for(int k = 0; k < (1 << m); k++){
if((k >> (m - 1)) & 1) dp[now][(k << 1) ^ (1 << m)] += dp[!now][k];
if(!((k >> (m - 1)) & 1) && i) dp[now][(k << 1) ^ 1] += dp[!now][k];
if(((k >> (m - 1)) & 1) && !(k & 1) && j)
dp[now][(k << 1) ^ (1 << m) ^ 3] += dp[!now][k];
}
}
}
printf("%lld\n", dp[now][(1 << m) - 1]);
}
return 0;
}

M - Expedition(贪心)

题意:

给定每个加油站的位置和油量,每走一单元消耗一单元油,问最少需要经过多少加油站到达目的地。

分析:

贪心,原则就是直到走不到下一站,再在这个站加油。

代码:

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
#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn = 10055;
typedef pair<int, int>pii;
pii p[maxn];
priority_queue<int>q;
int main (void)
{
int n;cin>>n;
int d, f, L, P;
for(int i = 1; i <= n ; i++) {
cin >> d >>f;
p[i] = make_pair(d, f);
}
sort(p + 1, p + 1 + n);
cin >> L >> P;
int num = 0;
bool flag = false;
p[n + 1].first= L;
p[n + 1].second= 0;
p[0].first= 0;
for(int i = n + 1; i >= 1; i--){
q.push(p[i].second);
while(P < p[i].first - p[i - 1].first){
if(q.empty()){
flag = true;
break;
}
P += q.top();q.pop();
num++;
}
if(flag) break;
P-=p[i].first - p[i - 1].first;
}
if(flag) cout<<-1<<endl;
else cout<<num<<endl;
return 0;
}

N.Package Delivery(贪心)

题意:

从坐标为$0$的地方出发到坐标为$d$的终点,初始油箱是满的,途中有若干加油站,坐标为$xi$,每加一个单位的油收$pi$元,油箱最多装$n$个单位,问到达目的地最少需要多少元。

分析:

贪心,原则就是遇到便宜的就先把油加上,避免走到后面加更贵的油
那么我们怎么保证遇到的是便宜的呢?可以预处理一遍,倒着推一遍,记录每个加油站的后面的最近的比他便宜的,这样到达该加油站只要加到能走到下一个便宜的就好了,然后到达下一个便宜的再加油。。。依次下去,如果某个加油站后面没有比他更小的,那么直接加满,希望能用便宜的油多走一些路。

代码:

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
/*************************************************************************
> File Name: N.cpp
> Author: jiangyuzhu
> Mail: 834138558@qq.com
> Created Time: 2016/7/14 9:05:34
************************************************************************/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<stack>
#include<algorithm>
using namespace std;
#define sa scanf
#define pr printf
#define pl(x) cout<<#x<<":"<<x<<' ';
typedef pair<int,int> p;
typedef long long ll;
const int maxn = 2e5 + 5, oo = 0x3f3f3f3f;
p stop[maxn];
int nxt[maxn];
int main (void)
{
int d, n, m;sa("%d%d%d", &d, &n, &m);
int x, y;
for(int i = 1; i <= m; i++){
sa("%d%d", &x, &y);
stop[i] = p(x, y);
}
stop[0] = p(0, 0);
stop[m + 1] = p(d, 0);
sort(stop, stop + m + 1);
stack<int>s;
memset(nxt, -1, sizeof(nxt));
for(int i = m; i >= 1; i--){
while(!s.empty() && stop[i].second <= stop[s.top()].second) s.pop();
if(s.empty()) nxt[i] = m + 1;
else nxt[i] = s.top();
s.push(i);
}
nxt[0] = s.top();
int after;
ll ans = 0;
int now = n;
int dist;
for(int i = 0; i <= m + 1; i++){
if(now < 0) return puts("-1"), 0;
dist = stop[nxt[i]].first - stop[i].first;
if(dist > now) after = dist;
else after = now;
if(after > n) after = n;
ans += (after - now) * 1ll * stop[i].second;
after -= stop[i + 1].first - stop[i].first;
now = after;
}
pr("%I64d\n", ans);
return 0;
}

O. BerDonalds(图绝对中心)

题意:

求图的绝对中心,即在边上或者顶点中选一个点$X$,使得该点到图中顶点的最远距离$f(X)$最小。

分析:

参考非常透彻的讲解 见C题

=== 我是搬运工===

首先$floyd$求出任意两点之间的最短路。
其次,根据题意,对于某一点$X,设其在线段$u-v$上,与点$u$的距离为$x$,对于任意点$w$,我们有f(X)=max{min{d[u][w]+x, d[v][w]+L_{u,v}-x}}$。
下面来分析$f(X)$的性质,对于固定的线段$u-v$,我们可以画出$f(X)关于x$的函数图像,为方便比较,把$w$们放在一个坐标系中,由于我们要求的是到其他点的最大值,所以对于某个$x$,只要保留该$x$对应的最大的$f(x)$值即可,也就是函数图像中最上面的点,如资料中实线所示。
$f(X)$的最小值就是实线中的最低点,观察图可知,这个最小值必然在交叉点上,即$L_1,L_2,L_3…$。
根据函数图像,存在交叉点的前提是$d[u][w_j] > d[u][w_k] 并且 d[v][w_j] < d[v][w_k]$,且有$d[u][w_j] + L_{u,v} - x = d[v][w_k] + x$,而此时的$f(X)= \frac{d[u][w_j] + L_{u,v} - x + d[v][w_k]+ x}{2} = \frac{d[u][w_j] + L_{u,v} + d[v][w_k]}{2}$
这样我们枚举每一条边,然后对于$d[u][w]$从大到小排个序,扫一遍,按照上述式子答案就出来了。

代码:

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
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<algorithm>
using namespace std;
typedef pair<int, int>p;
const int maxn = 2e2 + 5, oo = 0x3f3f3f3f ;
int dist[maxn][maxn];
struct EDGE{
int from, to, val;
}edge[maxn * maxn];
p node[maxn];
int tot;
int main (void)
{
int n, m;scanf("%d%d", &n, &m);
int a, b, w;
tot = 0;
memset(dist, 0x3f, sizeof(dist));
for(int i = 0; i < m; i++){
scanf("%d%d%d", &a, &b, &w);
a--;b--;
dist[a][b] = dist[b][a] = w;
edge[tot++] = (EDGE){a, b, w};
}
for(int k = 0; k < n; k++){
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
if(i == j) dist[i][j] = 0;
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
}
}
}
int ans = oo;
int tmp;
for(int i = 0; i < tot; i++){
int u = edge[i].from, v = edge[i].to;
for(int j = 0; j < n; j++){
node[j] = p(dist[j][u], dist[j][v]);
}
tmp = n - 1;
sort(node, node + n);
ans = min(ans, node[n - 1].first * 2);
for(int j = n - 2; j >= 0; j--){
if(node[j].second > node[tmp].second){
ans = min(ans, node[tmp].second + node[j].first + edge[i].val);
tmp = j;
}
}
ans = min(ans, node[tmp].second * 2);
}
printf("%.10f\n", (double)ans / 2.0);
return 0;
}

P - Hack it!(构造)

题意:

给定$a$,求区间,使得区间内所有数的数字和是$a$的倍数。

分析:

好吧,看起来很数位dp呀。。完全没头绪的想数位d好久。
其实是个找规律题。
首先有序列
$x, x + 1, x + 2…x + 10^k-1$
$x + 1, x + 2…x + 10^k-1,x+10^k$
我们发现$x$的出现的次数一样,而由于$10^k$的各位之和为$1$,所以下面的序列比上面的正好多一,而这与$x$大小无关,所以对于$10^k>x$,每次将序列右移一位,和就增加一!
那么我们最初可以找一个序列,为了保证$10^k大于x$,我们直接取到最大值即$10^18$,先求出$[1,10^{18}]$模$a$的余数,然后将序列右移$a-t$即可保证现在的区间模$a$余数为0。
那么怎么求$[1,10^{x}]$,这里有一个规律,写几位就能发现答案为$45x10^{x-1}$,
然后求模的时候拆起来边模边乘防止爆掉。

代码:

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
/*************************************************************************
> File Name: P.cpp
> Author: jiangyuzhu
> Mail: 834138558@qq.com
> Created Time: 2016/7/14 13:01:26
************************************************************************/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<algorithm>
#define sa(n) scanf("%d", &(n))
using namespace std;
typedef long long ll;
const ll maxm = 1e17;
int main (void)
{
ll a;scanf("%I64d" ,&a);
ll t = 5 * (9 * (18 * maxm % a) % a) % a;
printf("%I64d %I64d\n", a - t, maxm * 10 - 1 + a - t);
return 0;
}

Q - Runaway to a Shadow【计算几何】

题意:

给定小强初始位置,以及若干圆的圆心和半径。已知小强以均等概率选择前进方向,给定速度和时间,做匀速直线运动。问小强在给定时间内走进圆的概率。

分析:

建立坐标系,以小强初始位置为原点,只要求出以原点为圆心,半径为$T\times V$的圆$O$与其他每个圆相交的部分构成的弧度,再取个并集,那么概率即为$并集大小/2\pi$。
如何求弧度并集?利用余弦定理求出相交部分的弧度,注意弧度存在范围,当原点与交点的连线$L$与圆相切的时候,弧度到达最大,那么在计算的时候只要将$L$跟切线长取个最小值即可~
【沙茶如我以为这样就结束了。
注意!角弧度范围为$[0,2\pi]$,不要忘记对于$a-b \lt 0 和 a+b \gt 2\pi$的情况进行一下处理!orz

代码:

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
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<cmath>
#include<algorithm>
using namespace std;
typedef pair<double, double> pd;
const int maxn = 1e5 + 5;
double pi = acos(-1.0), eps = 1e-8;
struct POINT{
double x, y, r;
double dist(){
return x * x + y * y;
}
double angel(){
double a = atan2(y, x);
if(a < -eps) a += 2 * pi;
return a;
}
}p[maxn];
int main (void)
{
double x0, y0, V, T;
int n;
scanf("%lf%lf%lf%lf%d", &x0, &y0, &V, &T, &n);
double x, y, r;
double mx = T * V;
for(int i = 0; i < n; i++){
scanf("%lf%lf%lf", &x, &y, &r);
x -= x0; y -=y0;
p[i] = (POINT){x, y, r};
}
vector<pd>v;
for(int i = 0; i < n; i++){
if(p[i].dist() - p[i].r * p[i].r < eps) return printf("1.000000000\n"), 0;
if(sqrt(p[i].dist()) - eps > p[i].r + mx) continue;
double d1 = min(sqrt(p[i].dist() - p[i].r * p[i].r), mx);
double b = acos((p[i].dist() + d1 * d1 - p[i].r * p[i].r) / (2.0 * sqrt(p[i].dist()) * d1));
double a = p[i].angel();
if(a + b - eps > 2 * pi){
v.push_back(pd(a - b, 2 * pi));
v.push_back(pd(0, a + b - 2 * pi));
}else if(a - b < -eps){
v.push_back(pd(0, a + b));
v.push_back(pd(a - b + 2 * pi, 2 * pi));
}else{
v.push_back(pd(a - b, a + b));
}
}
if(!v.size()) return printf("0.000000000\n"), 0;
sort(v.begin(), v.end());
double ans = 0.0;
double l = v[0].first;
r = v[0].second;
int sz = v.size();
for(int i = 1; i < sz; i++){
if(v[i].first + eps > r){
ans += r - l;
l = v[i].first;
}
if(v[i].second + eps > r){
r = v[i].second;
}
}
ans += r - l;
printf("%.10f\n", ans / (2.0 * pi));
return 0;
}

R - yy math problem(数学)

题意:

求$1/n$小数部分的循环节和非循环节长度。

分析:

挺有意思的题,我是直接模拟除法的过程,那么余数相等即为循环,我们只要找到最先发生余数相同的两个位置,之间的距离即为循环节的长度。注意不要每次都$memset$,会爆,直接$for$遍历一遍。

代码:

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
/*************************************************************************
> File Name: R.cpp
> Author: jiangyuzhu
> Mail: 834138558@qq.com
> Created Time: 2016/7/14 10:35:35
************************************************************************/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<algorithm>
#define sa(n) scanf("%d", &(n))
using namespace std;
const int maxn = 1e7 + 5;
int pos[maxn];
int main (void)
{
int T;sa(T);
while(T--){
int n;sa(n);
if(n == 1){
puts("0 0");
continue;
}
for(int i = 1; i <= n; i++){
pos[i] = 0;
}
int t;
int x = 1;
int i = 1;
while(!pos[x] && x){
pos[x] = i++;
x = x * 10 % n;
}
int ans1, ans2;
if(!x){
ans2 = 0;
ans1 = i - 1;
}else{
ans2 = i - pos[x];
ans1 = pos[x] - 1;
}
printf("%d %d\n", ans1, ans2);
}
return 0;
}
文章目录
  1. 1. A - SPF(割点)
    1. 1.1. 题意:
    2. 1.2. 分析:
    3. 1.3. 代码:
  2. 2. B - Boonie and Clyde(割点)
    1. 2.1. 题意:
    2. 2.2. 分析:
    3. 2.3. 代码:
  3. 3. D - Matrix Decompressing【网络流】
    1. 3.1. 题意:
    2. 3.2. 分析:
    3. 3.3. 代码:
  4. 4. E - Pie(二分)
    1. 4.1. 题意:
    2. 4.2. 分析:
    3. 4.3. 代码:
  5. 5. F - K Best(二分)
    1. 5.1. 题意:
    2. 5.2. 分析:
    3. 5.3. 代码:
  6. 6. G - Median(二分)
    1. 6.1. 题意:
    2. 6.2. 分析:
    3. 6.3. 代码:
  7. 7. H - Showstopper(二分)
    1. 7.1. 题意:
    2. 7.2. 分析:
    3. 7.3. 代码:
  8. 8. I - Censored!(AC自动机+DP+大数)
    1. 8.1. 题意:
    2. 8.2. 分析:
    3. 8.3. 代码:
  9. 9. K - Palindromes
    1. 9.1. 题意:
    2. 9.2. 分析:
    3. 9.3. 代码:
  10. 10. L - Tiling Dominoes(轮廓线dp)
    1. 10.1. 题意:
    2. 10.2. 分析:
    3. 10.3. 代码:
  11. 11. M - Expedition(贪心)
    1. 11.1. 题意:
    2. 11.2. 分析:
    3. 11.3. 代码:
  12. 12. N.Package Delivery(贪心)
    1. 12.1. 题意:
    2. 12.2. 分析:
    3. 12.3. 代码:
  13. 13. O. BerDonalds(图绝对中心)
    1. 13.1. 题意:
    2. 13.2. 分析:
    3. 13.3. 代码:
  14. 14. P - Hack it!(构造)
    1. 14.1. 题意:
    2. 14.2. 分析:
    3. 14.3. 代码:
  15. 15. Q - Runaway to a Shadow【计算几何】
    1. 15.1. 题意:
    2. 15.2. 分析:
    3. 15.3. 代码:
  16. 16. R - yy math problem(数学)
    1. 16.1. 题意:
    2. 16.2. 分析:
    3. 16.3. 代码: