C.Errich-Tac-Toe

题目描述

 

解法

先考虑 \(\tt easy\space version\),针对 \(\lfloor\frac{k}{3}\rfloor\) 来构造,可以把整张图三染色,一定有一种颜色满足格子 X 的数量不超过 \(\lfloor\frac{k}{3}\rfloor\),把这种颜色的X全部改成O即可。

对于 \(\tt hard\space version\),还是沿用染色的思路,我们让任意相邻的三个格子出现XO,也就是把某种颜色全部改成X,某种颜色全部改成O,那么我们让出现次数最多的颜色不变,这样剩下不超过 \(\frac{2k}{3}\) 的格子,XOOX一定有一种能让改变的格子不超过 \(\frac{1}{2}\)(因为XXOO的贡献是 \(1\);XOOX对某一个贡献是 \(2\),对另一个没有贡献),所以改变的总格子数不超过 \(\lfloor\frac{k}{3}\rfloor\)

实现的时候讨论每种修改方案,看哪种满足条件即可。

总结

限制出现在相邻格子上,染色是很好的解决方案。

#include <cstdio>
#include <iostream>
using namespace std;
const int M = 305;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int T,n;char a[M][M],b[M][M];
int check(string s)
{
	int c1=0,c2=0;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
		{
			b[i][j]=a[i][j];
			if(a[i][j]!='.' && s[(i+j)%3]!='.')
				b[i][j]=s[(i+j)%3];
		}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(a[i][j]!='.')
				c1++,c2+=(a[i][j]!=b[i][j]);
	return c2<=c1/3;
}
void print()
{
	for(int i=1;i<=n;i++,puts(""))
		for(int j=1;j<=n;j++)
			printf("%c",b[i][j]);
}
void work()
{
	n=read();
	for(int i=1;i<=n;i++)
		scanf("%s",a[i]+1);
	if(check(".XO")) print();
	else if(check(".OX")) print();
	else if(check("X.O")) print();
	else if(check("O.X")) print();
	else if(check("OX.")) print();
	else if(check("XO.")) print();
}
signed main()
{
	T=read();
	while(T--) work();
}

H.Multithreading

题目描述

点此看题

解法

首先考虑没有问号的情况怎么计算答案,设 \(b_o,b_e\) 分别表示奇数位置和偶数位置上 \(b\) 颜色的个数,\(w_o,w_e\) 类似,其实贪心都可以猜出结论:\(f(c)=\frac{1}{2}|b_o-b_e|\),设 \(|b_o-b_e|=2k\),证明:

  • 首先证明 \(f(c)\leq k\),可以通过构造一组有 \(k\) 个异色交点的解来完成,我们先把相邻两个位置奇偶性不同的同色点连接起来,这时候一定不会产生异色交点。剩下的点满足 \(b_o=2k,w_e=2k\),那么我们按照 \(bwbw...bwbw\) 这样相邻的同色点连接,发现会产生 \(k\) 个交点。
  • 然后证明 \(f(c)\geq k\),我们声明最优解中不存在同色交点,因为如果存在可以通过调整使之不交,并且异色交点个数不增。所以就可以假设不存在同色交点,因为 \(|b_o-b_e|=2k\),所以必然会有 \(k\) 条连接奇偶性相同位置的边,并且这些边会把原图分成两部分满足点数都为奇数,那么必然存在交点,由于不是同色交点,那么一定是异色交点,所以 \(f(c)\geq k\)

这个结论可以用于计数,设 \(F\) 为?的个数,\(F_o\) 为偶数位置?的个数,我们枚举?中有 \(i\) 个位置,如果是偶数位置染色为 \(b\),否则染色成 \(w\),其他位置用相反的方法染色。设 \(i\) 中染色 \(b\) 的个数为 \(a\),那么 \(b\) 颜色在偶数位置的个数是 \(b_e+a\),在奇数位置的个数是 \(b_o+F_o-i+a\)(因为 \(i-a\) 是奇数位置用掉的?个数),所以:

 

\[f(c)=\frac{1}{2}|b_e+a-(b_o+F_o-i+a)|=\frac{1}{2}|b_e-b_o-F_o+i| \]

 

设 \(x=b_o+F_o-b_e=\frac{n}{2}-w_o-b_e\),所以 \(f(c)=\frac{1}{2}|x-i|\),\(i\) 对应的方案数有 \({F\choose i}\) 种,所以答案是:

 

\[\frac{1}{2^F}\sum_{0\leq i\leq F\\i=x\bmod2}|x-i|\cdot {F\choose i} \]

 


从 \(\tt easy\space version\) 继续,暂且忽略 \(\frac{1}{2^F}\) 这个系数,我们把绝对值拆掉(默认 \(i=x\bmod 2\)):

 

\[\sum_{0\leq i\leq x} x{F\choose i}-\sum_{0\leq i\leq x} i{F\choose i}+\sum_{x\leq i\leq F}i{F\choose i}-\sum_{x\leq i\leq F}x{F\choose i} \]

 

首先我们把组合数前面的系数拿掉,对于 \(x{F\choose i}\) 直接提到前面去,\(i{F\choose i}=F{F-1\choose i-1}\),把 \(F\) 拿到前面去。

还要解决 \(i=x\bmod 2\) 的问题,因为 \({F\choose i}={F-1\choose i}+{F-1\choose i-1}\) 可以直接转成前缀和的形式,组合数前缀和是很容易修改的,只需要使用这个恒等式:

 

\[\sum_{i=0}^k{F+1\choose i}=2\sum_{i=0}^k{F\choose i}-{F\choose k} \]

 

总结

一般这种题都有结论来支持计数,要大胆猜结论。

推式子的时候注意绝对值可以拆掉,如果式子的主体是组合数,可以尝试把他变成组合数前缀和的形式。

#include <cstdio>
const int M = 200005;
const int MOD = 998244353;
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,x,F,ans,inv[M],fac[M];char s[M];
int Abs(int x)
{
	return x>0?x:-x;
}
void init()
{
	inv[0]=inv[1]=fac[0]=1;
	for(int i=2;i<=n;i++) inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
	for(int i=1;i<=n;i++) inv[i]=inv[i-1]*inv[i]%MOD;
	for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%MOD;
}
int C(int n,int m)
{
	return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}
signed main()
{
	n=read();m=read();
	init();
	scanf("%s",s+1);
	x=n/2;
	for(int i=1;i<=n;i++)
	{
		F+=(s[i]=='?');
		if(i%2 && s[i]=='w') x--;
		if(i%2==0 && s[i]=='b') x--;
	}
	for(int i=0;i<=F;i++)
		if(i%2==(x%2+2)%2)
			ans=(ans+Abs(x-i)*C(F,i))%MOD;
	for(int i=1;i<=F;i++)
		ans=ans*inv[2]%MOD;
	printf("%lld\n",ans);
}