Codeforces Round 692 A ~ C 题解

A. Peaceful Rooks

题意

给定一个 $n \times n$ 的棋盘和 $m$ 个棋子,任意两个棋子不在同一行或同一列。每次可以水平或竖直移动一个棋子,且移动后仍然任意两个棋子不在同一行或同一列,求把所有棋子移动到主对角线上所需的最少次数。

$n \leq 10^5$

解法

假设我们现在想把在 $(x,y)$ 的棋子移动到 $(x,x)$,如果 $x$ 列上有棋子 $(z,x)$,我们就需要先移走这个棋子,显然可以直接尝试移动到 $(z,z)$。棋子的支配关系会形成一些链或环,链所需步数就是棋子个数,环所需步数是个数 $+1$,并查集统计即可。注意判断本来就在对角线上的棋子。

code

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
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1000001;
int n,m,fa[N];
bool h[N];
int find(int x)
{
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) fa[i]=i;
int s=0;
for(int i=1;i<=m;++i)
{
int x,y;
scanf("%d%d",&x,&y);
if(x!=y) ++s;
else fa[x]=0;
fa[find(x)]=find(y);
}
for(int i=1;i<=n;++i)
{
if(find(i)==i) ++s;
}
printf("%d\n",s-(n-m));
}
return 0;
}

B. Grime Zoo

题意

给定一个由 01? 组成的字符串 $S$ 和参数 $x,y$,你需要在所有字符为 ? 的位置填入字符 0 或者 1

我们规定这个字符串的价值为:01 子序列的数量 $\times x + $ 10 子序列的数量 $\times y$。

求字符串价值最小值。

$|S| \leq 10^5,0 \leq x,y \leq 10^6$

解法

对于一个 ?,如果填 0,贡献为 $y \times $ 前面 $1$ 的个数 $+$ $x \times $ 后面 $1$ 的个数,填 1 的贡献为 $x \times $ 前面 $0$ 的个数 $+$ $y \times $ 后面 $0$ 的个数。由于越靠后的 ?,前面的 01 个数单调不降,后面的 01 个数单调不增,所以只可能是 00..1111..00 的情况。

假定是 00..11 的情况,我们可以先算出全部填 1 的价值,然后从前往后把 ? 变为 0,每次处理变化的贡献即可。11..00 的情况同理。

code

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
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=1000001;
int n,b1[N],b2[N],b3[N],c1[N],c2[N],c3[N];
ll k1,k2;
char a[N];
int main()
{
scanf("%s%lld%lld",a+1,&k1,&k2);
n=strlen(a+1);
for(int i=1;i<=n;++i)
{
b1[i]=b1[i-1];
b2[i]=b2[i-1];
b3[i]=b3[i-1];
if(a[i]=='0') ++b1[i];
if(a[i]=='1') ++b2[i];
if(a[i]=='?') ++b3[i];
}
for(int i=n;i>=1;--i)
{
c1[i]=c1[i+1];
c2[i]=c2[i+1];
c3[i]=c3[i+1];
if(a[i]=='0') ++c1[i];
if(a[i]=='1') ++c2[i];
if(a[i]=='?') ++c3[i];
}
ll w=0;
for(int i=1;i<=n;++i)
{
if(a[i]=='0') w+=(b2[i-1]+b3[i-1])*k2;
else w+=b1[i-1]*k1;
}
ll s=w;
for(int i=1;i<=n;++i)
{
if(a[i]=='?')
{
w-=(b1[i-1]+b3[i-1])*k1+c1[i+1]*k2;
w+=b2[i-1]*k2+(c2[i+1]+c3[i+1])*k1;
}
s=min(s,w);
}
w=0;
for(int i=1;i<=n;++i)
{
if(a[i]=='0'||a[i]=='?') w+=b2[i-1]*k2;
else w+=(b1[i-1]+b3[i-1])*k1;
}
s=min(s,w);
for(int i=1;i<=n;++i)
{
if(a[i]=='?')
{
w-=(b2[i-1]+b3[i-1])*k2+c2[i+1]*k1;
w+=b1[i-1]*k1+(c1[i+1]+c3[i+1])*k2;
}
s=min(s,w);
}
printf("%lld",s);
return 0;
}

C. Poman Numbers

题意

给定一个只含小写字母的字符串 $S$。

定义 $f(S)$。如果 $|S|=1$,设这个小写字母的是第 $t$ 个(a 为 $0$,b 为 $1$,z 为 $25$),$f(S)=2^t$。否则 $f(S)=-f(S[1,m])+f(S[m+1,|S|])$,$m$ 在 $[1,|S|-1]$ 中自由选择。

求是否存在一种构造 $m$ 的方案(每一步的 $m$ 可以不同)使得 $f(S)=T$。

解法

我们观察 $S$ 每一位最后可能的符号。首先最后一位在每一步中都是 $+$,所以符号一定是 $+$。倒数第二位后面只有一位,所有只可能有一次 $-$,最终符号一定是 $-$。倒数第三位就两种均可,我们可以猜测除了最后两位位符号任取。

我们先把最后两位的贡献在 $T$ 中减去,接下来就是给一些 $2$ 的次幂定符号,使得和为 $T$。我们从大到小排序,当前的数是 $x$,剩下的数的和 $T’< 0$,$+x$ 一定比 $-x$ 更优,因为如果 $-x$ 后仍然能使最后 $T’ = 0$,又因为是 $2$ 的次幂,所以后面的数一定可以拼出 $x$,$+x$ 也有解。同理 $T’>0$ 则 $-x$。

code

code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1000001;
int n;
ll k;
char a[N];
int main()
{
scanf("%d%lld%s",&n,&k,a+1);
k+=(1ll<<(a[n-1]-'a'))-(1ll<<(a[n]-'a'));
sort(a+1,a+n-1);
for(int i=n-2;i>=1;--i)
{
if(k<=0) k+=(1ll<<(a[i]-'a'));
else k-=(1ll<<(a[i]-'a'));
}
if(k==0) printf("Yes\n");
else printf("No\n");
return 0;
}