Codeforces Round #639 (Div.2)

比赛链接 / 官方题解链接

这场出题人真惨~

A. Puzzle Pieces

Solution

若 $n=1$ 或 $m=1$ 或 $n=m=2$,那么可行;否则不可行,因为无法拼出 $2\times3$ 的格子。

Code

>folded
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
#include<bits/stdc++.h>

using namespace std;

template<typename T>void read(T&x){x=0;int fl=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')
fl=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}x*=fl;}
template<typename T,typename...Args>inline void read(T&t,Args&...args){read(t);read(args...);}

typedef long long LL;
typedef vector<int> vi;
typedef pair<int, int> pii;
#define mp(x, y) make_pair(x, y)
#define pb(x) emplace_back(x)

int T, n, m;

int main(){
for(read(T); T; T--){
read(n, m);
if(n == 1) puts("YES");
else if(m == 1) puts("YES");
else if(n == 2 && m == 2) puts("YES");
else puts("NO");
}
return 0;
}

B. Card Constructions

Solution

可以推导出通项公式:$a_n=\frac{3n^2+n}{2}$,那么不断二分最大金字塔数量。

Code

>folded
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<bits/stdc++.h>

using namespace std;

template<typename T>void read(T&x){x=0;int fl=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')
fl=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}x*=fl;}
template<typename T,typename...Args>inline void read(T&t,Args&...args){read(t);read(args...);}

typedef long long LL;
typedef vector<int> vi;
typedef pair<int, int> pii;
#define mp(x, y) make_pair(x, y)
#define pb(x) emplace_back(x)

LL a[100005];
int T, n;

int main(){
int id = 0;
for(int i = 1; i <= 100000; i++){
a[++id] = (3ll * i * i + i) / 2;
if(a[id] > 1e9) break;
}
for(read(T); T; T--){
read(n);
int ans = 0;
while(n){
int p = lower_bound(a+1, a+id, n) - a;
if(a[p] > n) p--;
if(p == 0) break;
n -= a[p];
ans++;
}
printf("%d\n", ans);
}
return 0;
}

C. Hilbert’s Hotel

Solution

如果 $0\sim n-1$ 在变换后恰好占据了模 $n$ 的不同余数的位置,那么可行;否则不可。

Code

>folded
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
#include<bits/stdc++.h>

using namespace std;

template<typename T>void read(T&x){x=0;int fl=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')
fl=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}x*=fl;}
template<typename T,typename...Args>inline void read(T&t,Args&...args){read(t);read(args...);}

typedef long long LL;
typedef vector<int> vi;
typedef pair<int, int> pii;
#define mp(x, y) make_pair(x, y)
#define pb(x) emplace_back(x)

const int N = 200005;

int T, n, a[N], s[N];

int main(){
for(read(T); T; T--){
read(n);
memset(s, 0, sizeof s);
for(int i = 0; i < n; i++){
read(a[i]);
int res = ((i + a[i]) % n + n) % n;
s[res]++;
}
bool b = true;
for(int i = 0; i < n; i++)
if(s[i] != 1){
b = false;
break;
}
if(b) puts("YES");
else puts("NO");
}
return 0;
}

D. Monopole Magnets

Solution

如果某一行或某一列有间隔的黑块,那么一定无解——因为这一行必须要放一个 $S$,而这个 $S$ 可以把走到黑块上的 $N$ 勾引到白块上去。

如果某一行是全白色,那么必须要有一列也是全白色,否则这一行放不了 $S$;某一列全白色同理。

上述两个条件满足后,只需要在所有黑块上放 $S$,那么在每个黑色连通块上放一个 $N$ 就可以了。所以答案就是黑色连通块的个数。

Code

>folded
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
#include<bits/stdc++.h>

using namespace std;

template<typename T>void read(T&x){x=0;int fl=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')
fl=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}x*=fl;}
template<typename T,typename...Args>inline void read(T&t,Args&...args){read(t);read(args...);}

typedef long long LL;
typedef vector<int> vi;
typedef pair<int, int> pii;
#define mp(x, y) make_pair(x, y)
#define pb(x) emplace_back(x)

const int N = 1005;

int n, m;
char g[N][N];
int l[N][N], u[N][N];
bool brow[N], bcol[N];

int fa[N*N];
int findfa(int x){ return fa[x] == x ? x : fa[x] = findfa(fa[x]); }
inline void unionn(int x, int y){
if(findfa(x) != findfa(y))
fa[findfa(y)] = findfa(x);
}

int main(){
read(n, m);
for(int i = 1; i <= n; i++)
scanf("%s", g[i] + 1);
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
if(j == 1) l[i][j] = 0;
else l[i][j] = g[i][j-1] == '#' ? j-1 : l[i][j-1];
if(i == 1) u[i][j] = 0;
else u[i][j] = g[i-1][j] == '#' ? i-1 : u[i-1][j];
if(g[i][j] == '#'){
if(l[i][j] != 0 && l[i][j] != j-1) return puts("-1"), 0;
if(u[i][j] != 0 && u[i][j] != i-1) return puts("-1"), 0;
brow[i] = bcol[j] = true;
}
}
}
for(int i = 1; i <= n; i++){
if(brow[i] == false){
bool ok = false;
for(int j = 1; j <= m; j++)
if(bcol[j] == false)
ok = true;
if(!ok) return puts("-1"), 0;
}
}
for(int j = 1; j <= m; j++){
if(bcol[j] == false){
bool ok = false;
for(int i = 1; i <= n; i++)
if(brow[i] == false)
ok = true;
if(!ok) return puts("-1"), 0;
}
}
for(int i = 1; i <= n*m; i++) fa[i] = i;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
int p = (i - 1) * m + j;
if(g[i][j] == '.') continue;
if(i > 1 && g[i-1][j] == '#') unionn(p, p-m);
if(i < n && g[i+1][j] == '#') unionn(p, p+m);
if(j > 1 && g[i][j-1] == '#') unionn(p, p-1);
if(j < m && g[i][j+1] == '#') unionn(p, p+1);
}
}
int cnt = 0;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
cnt += g[i][j] == '#' && findfa((i-1)*m+j) == (i-1)*m+j;
printf("%d\n", cnt);
return 0;
}

E. Quantifier Question

Solution

如果对于条件 $a<b$,我们连一条从 $a$ 到 $b$ 的有向边,那么得到了一个有向图。如果该有向图有环,那么显然无解;否则该有向图是一个 $DAG$. 对于每一个点,我们可以通过拓扑序和逆拓扑序得到与它具有比较关系的最小编号,如果这个最小编号小于它本身,那么这个点必须填 $\exists$,否则填 $\forall$.

Code

>folded
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<bits/stdc++.h>

using namespace std;

template<typename T>void read(T&x){x=0;int fl=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')
fl=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}x*=fl;}
template<typename T,typename...Args>inline void read(T&t,Args&...args){read(t);read(args...);}

typedef long long LL;
typedef vector<int> vi;
typedef pair<int, int> pii;
#define mp(x, y) make_pair(x, y)
#define pb(x) emplace_back(x)

const int N = 200005;

int n, m;
char ans[N];

vi edge[2][N];
int ind[2][N];

int mn[2][N], tot;
void topo(int k){
queue<int> q;
for(int i = 1; i <= n; i++)
if(!ind[k][i])
q.push(i);
while(!q.empty()){
int cur = q.front(); q.pop();
tot++;
for(auto to: edge[k][cur]){
ind[k][to]--;
mn[k][to] = min(mn[k][to], mn[k][cur]);
if(!ind[k][to]) q.push(to);
}
}
}

int main(){
read(n, m);
for(int i = 1; i <= m; i++){
int j, k; read(j, k);
edge[0][j].pb(k); ind[0][k]++;
edge[1][k].pb(j); ind[1][j]++;
}
for(int i = 1; i <= n; i++) mn[0][i] = mn[1][i] = i;
topo(0);
if(tot < n){ puts("-1"); return 0; }
topo(1);
int cnt = 0;
for(int i = 1; i <= n; i++){
if(mn[0][i] == i && mn[1][i] == i)
ans[i] = 'A', cnt++;
else ans[i] = 'E';
}
printf("%d\n", cnt);
for(int i = 1; i <= n; i++) putchar(ans[i]);
puts("");
return 0;
}

F. Résumé Review

Solution

【参考官方题解】如果我们将第 $i$ 个数从 $x-1$ 增加到 $x$,那么答案增量为:

这是一个关于 $x$ 的递减函数。如果正向考虑,我们需要每次找到最大的增量,使该数加一,但是这肯定超时。正向麻烦时考虑反向——我们二分一个增量下限 $A$,对 $1\sim n$ 的每一个数来说,只要增量大于等于 $A$,就往上加——这一点可以二分上述公式中的 $x$ 完成。统计我们加的数 $cnt$,如果 $cnt>=k$,那么增大下限 $A$,使 $cnt$ 减小;否则减小下限 $A$,使 $cnt$ 增大。如此二分可以最终找到最大增量下限 $A$ 使得 $cnt$ 刚好大于或等于 $k$. 如果 $cnt=k$,那便是极好的;如果 $cnt>k$,我们一定可以找到一些数,它们的最后一个增量恰好等于 $A$——否则的话这个 $A$ 还可以增大,就不是最大增量下限了。对于这些数,我们往回撤销掉它们的增量,直到 $cnt=k$.

Code

>folded
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
#include<bits/stdc++.h>

using namespace std;

template<typename T>void read(T&x){x=0;int fl=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')
fl=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}x*=fl;}
template<typename T,typename...Args>inline void read(T&t,Args&...args){read(t);read(args...);}

typedef long long LL;
typedef vector<int> vi;
typedef pair<int, int> pii;
#define mp(x, y) make_pair(x, y)
#define pb(x) emplace_back(x)

const int N = 100005;

int n;
LL k, a[N], b[N], cnt;
vi v;

bool check(LL A){
cnt = 0;
v.clear();
for(int i = 1; i <= n; i++){
if(a[i] - 1 < A){ b[i] = 0; continue; }
LL l = 1, r = a[i];
while(l < r){
LL mid = (l + r + 1) / 2;
if(a[i] - 3 * mid * mid + 3 * mid - 1 >= A)
l = mid;
else r = mid - 1;
}
b[i] = l;
cnt += l;
if(a[i] - 3 * l * l + 3 * l - 1 == A)
v.pb(i);
}
return cnt >= k;
}

int main(){
read(n, k);
for(int i = 1; i <= n; i++) read(a[i]);
LL L = -4e18, R = 1e9;
while(L < R){
LL mid = (L + R + 1) / 2;
if(check(mid)) L = mid;
else R = mid - 1;
}
check(L);
for(auto i: v)
if(cnt > k && b[i] > 0)
cnt--, b[i]--;
for(int i = 1; i <= n; i++)
printf("%lld ", b[i]);
return 0;
}
作者

xyfJASON

发布于

2020-05-07

更新于

2020-12-20

许可协议

评论