Codeforces Round 664(Div.2)题解

本人没有打这场 CF,但是场外看到这套题觉得很多题都很有趣,于是写个题解分享一下。

本场比赛链接:https://codeforces.com/contest/1395

A - Boboniu Likes to Color Balls

这些字符可以构成回文串当且仅当至少三种字符的个数要是偶数。

然后我们又发现一次操作相当于奇偶互换,但是又没有这么简单,要特判 r,g,b=0r,g,b=0 的情况。记当前个数是偶数的字符种数为 xx,分两类情况讨论:

  • x>2x>2,则肯定可以满足。
  • x<3x<3,这时再分成三类讨论:
    1、r=0r=0g=0g=0b=0b=0,则没有办法执行操作,永远无法满足回文的条件。
    2、r,g,b0,x=2r,g,b\ne 0,x=2,则不论这么操作都是两奇两偶,无法满足回文的条件。
    3、r,g,b0,x=1r,g,b\ne 0,x=1,则只需要进行一次操作,就可以满足回文的条件。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
using namespace std;

int tt,r,g,b,w;

int main()
{
scanf("%d",&tt); int o;
while (tt--)
{
scanf("%d%d%d%d",&r,&g,&b,&w);
o=(r%2==0)+(g%2==0)+(b%2==0)+(w%2==0);
if (o>2) puts("Yes");
else if (!r||!g||!b) puts("No");
else
{
if (o==2) puts("No");
else puts("Yes");
}
}

return 0;
}

B - Boboniu Plays Chess

这道题我们需要先把当前所在行的所有格子全都走一遍,然后从第一行开始,按顺序走完所有未走过的行,但是每走完一行,接下来走的方向需倒过来(比如你某一行是从左往右的方向走的,那么你下一未走过的行就要从右往左方向走)就可以了。(简单来说就是 S 形走位就可以了)

但是还有更简单的做法,注意到 n,m100n,m\le 100,所以直接 dfs 即可。

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
#include <stdio.h>
#include <math.h>
#include <cstdlib>
#define il inline
using namespace std;
const int N=205;

int n,m,sx,sy;
int ok[N][N],p[N*N],q[N*N];

il void dfs(int x,int y,int c)
{
int i;
if (c>n*m){for (i=1; i<=n*m; i++) printf("%d %d\n",p[i],q[i]);exit(0);}
for (i=1; i<=n; i++) if (!ok[i][y])
ok[i][y]=1,p[c]=i,q[c]=y,dfs(i,y,c+1),ok[i][y]=0;
for (i=1; i<=m; i++) if (!ok[x][i])
ok[x][i]=1,p[c]=x,q[c]=i,dfs(x,i,c+1),ok[x][i]=0;
}

int main()
{
scanf("%d%d%d%d",&n,&m,&sx,&sy);

ok[p[1]=sx][q[1]=sy]=1,dfs(sx,sy,2);

return 0;
}

C - Boboniu and Bit Operations

首先,我们求出每个 ci,j=ai and bjc_{i,j}=a_i\ \mathrm{and}\ b_j

然后我们从高位到低位贪心,根据或运算的特点,只有全部是 00 才是 00,设当前位为 kk

那么如果不存在 i,ji,j,使得 ci,jc_{i,j} 的第 kk 位为 00,那么只能是 11,这时候计算贡献;如果存在 i,ji,j,使得 ci,jc_{i,j} 的第 kk 位位 00,那么这时候不需要计算贡献,而是把满足 ci,j=1c_{i,j}=1(i,j)(i,j) 删掉即可。

时间复杂度为 O(nmlog)O(nm\log),空间复杂度为 O(nm)O(nm)

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
#include <stdio.h>
#include <algorithm>
using namespace std;
const int N=205;

int n,m,ans,t,a[N],b[N][N];

int main()
{
scanf("%d%d",&n,&m); int i,j,k,o1,o2;
for (i=1; i<=n; i++) scanf("%d",a+i),t=max(t,a[i]);
for (j=1; j<=m; j++)
{
scanf("%d",&k),t=max(t,k);
for (i=1; i<=n; i++) b[i][j]=a[i]&k;
}

for (k=9; ~k; k--) if ((1<<k)<=t)
{
for (i=o1=1; i<=n; i++)
{
for (j=o2=1; j<=m; j++) if (b[i][j]>=0&&!(b[i][j]>>k&1)){o2--;break;}
if (o2){o1--;break;}
}
if (!o1) ans+=1<<k;
else{for (i=1; i<=n; i++) for (j=1; j<=m; j++) if (b[i][j]>>k&1) b[i][j]=-1;}
}
printf("%d",ans);

return 0;
}

D - Boboniu Chats with Du

我们按照与 mm 的大小关系分作两类,如果我们选了不超过 mm 中的 ss 个,那么我们就可以选超过 mm 中的 nsd+1\lceil \frac{n-s}{d+1} \rceil 个。

那么我们排个序,分别求出 pi,qip_i,q_i,表示不超过/超过 mm 中选 ii 的最大价值,则答案就是 max0iP{pi+qnid+1 }\max\limits_{0\le i\le P}\{p_i+q_{\lceil\frac{n-i}{d+1}\ \rceil}\},其中 PP 是不超过 mm 的个数。

时间复杂度为 O(nlogn)O(n\log{n}),瓶颈在排序上,空间复杂度为 O(n)O(n)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <algorithm>
#define int long long
using namespace std;
const int N=1e5+5;

int n,d,m,ans,a[N];
int b[N],s1,c[N],s2;

signed main()
{
scanf("%lld%lld%lld",&n,&d,&m),d++; int i;
for (i=1; i<=n; i++) scanf("%lld",a+i);
sort(a+1,a+n+1);
for (i=n; i; i--)
if (a[i]>m) s2++,c[s2]=c[s2-1]+a[i];
else s1++,b[s1]=b[s1-1]+a[i];

for (i=0; i<=s1; i++) ans=max(ans,b[i]+c[(n-i+d-1)/d]);
printf("%lld",ans);

return 0;
}

E - Boboniu Walks on Graph

这道题我觉得蛮有意思的。

首先肯定要将每条边的出边按照边权排序,这里的时间复杂度为 O(nlogn)O(n\log{n})

然后 dfs 的时间复杂度为 O(k!)O(k!)

我们还发现,(c1,c2,,ck)(c_1,c_2,\cdots,c_k) 合法等价于按照这个 kk 元组连完边后,每个点有且只有一条出边。

朴素判断是 O(n)O(n) 的,如果这样复杂度将无法接受,考虑怎么优化这个判断过程。

最直接的想法就是 bitset,时间复杂度为 O(nk!ω)O(\frac{nk!}{\omega}),空间复杂度为 O(n2ω)O(\frac{n^2}{\omega}),大力卡常是可以过的,但是这里不讲这个做法。

我们考虑预处理出 Si,jS_{i,j},表示 ci=jc_i=j 时会被占用的点的集合。那么我们 dfs 的时候只需要判断当前的集合与 Si,jS_{i,j} 的交集是否为空,最后所有的集合并完后是不是 1,2,,n{1,2,\cdots,n}

我们将集合哈希一下,转化为哈希值,这样就可以做到 O(1)O(1) 判断。

本人采用双哈希,防止被叉,时间复杂度为 O(nlogn+k!)O(n\log{n}+k!),空间复杂度为 O(n2+m)O(n^2+m)

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
#include <stdio.h>
#include <algorithm>
#include <vector>
#define il inline
#define ll long long
using namespace std;
const int N=2e5+5; const ll P1=998244353,P2=1e9+7;

int n,m,k; vector<pair<int,int> > e[N]; vector<int> v[10];
ll ans,s1,s2,t1[N],t2[N],p1[10][10],p2[10][10];

il void dfs(int c,ll p,ll q)
{
if (c>k){ans+=(p==s1&&q==s2);return;} int i;
for (i=0; i<c; i++) dfs(c+1,(p+p1[c][i])%P1,(q+p2[c][i])%P2);
}

int main()
{
scanf("%d%d%d",&n,&m,&k); int i,j,w;t1[0]=t2[0]=1;
for (i=1; i<=n; i++) s1=(s1+(t1[i]=t1[i-1]*2347623ll%P1))%P1,s2=(s2+(t2[i]=t2[i-1]*19260817ll%P2))%P2;
while (m--) scanf("%d%d%d",&i,&j,&w),e[i].push_back(make_pair(w,j));

for (i=1; i<=n; i++) sort(e[i].begin(),e[i].end()),v[e[i].size()].push_back(i);
for (i=1; i<=k; i++) for (j=0; j<i; j++)
for (auto w:v[i]) p1[i][j]=(p1[i][j]+t1[e[w][j].second])%P1,p2[i][j]=(p2[i][j]+t2[e[w][j].second])%P2;
dfs(1,0,0),printf("%lld",ans);

return 0;
}

F - Boboniu and String

这道题我觉得也蛮有意思的。

首先显然,串 SSTT 是相似的当且仅当两个串的字符 B 与字符 N 的个数都相同。

而操作等价于我们将串 SS 的字符 B 或者 N 的数量减少或增加一,或者让字符 BN 的数量同时增加或者减少一。

我们将字符 B 的数量当作横坐标,字符 N 的数量当作纵坐标,那么如果限制 kk 步就要相似,则如下范围内的串都可以满足要求:

pic

pic

这个平面区域我们用线性规划的方法,用一个不等式组表示出来:
设第 ii 个字符串的坐标为 (xi,yi)(x_i,y_i)

{xxi+kxxikyyi+kyyikxyxiyi+kxyxiyik\begin{cases}x\le x_i+k\\x\ge x_i-k\\y\le y_i+k\\y\ge y_i-k\\x-y\le x_i-y_i+k\\x-y\ge x_i-y_i-k\end{cases}

pic

我们二分答案,建出这 nn 个区域,判断这 nn 个区域有没有交即可。

接下来考虑构造一个方案:
假设我们如上求出了 x,y,xyx,y,x-y 的范围,分别为 x[X1,X2],y[Y1,Y2],xy[Z1,Z2]x\in [X_1,X_2],y\in [Y_1,Y_2],x-y\in [Z_1,Z_2]
那么显然有 x[Y1+Z1,Y2+Z2]x \in[Y_1+Z_1,Y_2+Z_2],与 [X1,X2][X_1,X_2] 取交集后求出 xx 的一个值,然后便有 y[xZ2,xZ1]y\in[x-Z_2,x-Z_1],与 [Y1,Y2][Y_1,Y_2] 取交集后求出 yy 的一个值,即可。

时间复杂度为 O(nlogn)O(n\log{n}),空间复杂度为 O(n)O(n)

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
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define il inline
using namespace std;
const int N=5e5+5;

int n,ans,x[N],y[N],X1,X2,Y1,Y2,Z1,Z2; char s[N];

il void work(int l)
{
int i; X1=Y1=Z1=-1e9,X2=Y2=Z2=1e9;
for (i=1; i<=n; i++)
X1=max(X1,x[i]-l),X2=min(X2,x[i]+l),
Y1=max(Y1,y[i]-l),Y2=min(Y2,y[i]+l),
Z1=max(Z1,x[i]-y[i]-l),Z2=min(Z2,x[i]-y[i]+l);
}

il int check(int l)
{
work(l);
if (X1>X2||Y1>Y2||Z1>Z2||X1-Y2>Z2||X2-Y1<Z1) return 0;
return 1;
}

int main()
{
scanf("%d",&n); int i,j,l,r,mid;
for (i=1; i<=n; i++)
{
scanf("%s",s+1),l=strlen(s+1);
for (j=1; j<=l; j++)
if (s[j]=='B') x[i]++;
else y[i]++;
}

for (l=0,r=N+N; l<=r; )
{
mid=l+r>>1;
if (check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
printf("%d\n",ans),work(ans),X1=max(X1,0),Y1=max(Y1,0);
l=min(X2,Y2+Z2),r=min(l-Z1,Y2);
while (l--) putchar('B'); while (r--) putchar('N');

return 0;
}