题目描述ios
解法c++
先考虑 \(\tt easy\space version\),针对 \(\lfloor\frac{k}{3}\rfloor\) 来构造,能够把整张图三染色,必定有一种颜色知足格子 X
的数量不超过 \(\lfloor\frac{k}{3}\rfloor\),把这种颜色的X
所有改为O
便可。ide
对于 \(\tt hard\space version\),仍是沿用染色的思路,咱们让任意相邻的三个格子出现X
和O
,也就是把某种颜色所有改为X
,某种颜色所有改为O
,那么咱们让出现次数最多的颜色不变,这样剩下不超过 \(\frac{2k}{3}\) 的格子,XO
或OX
必定有一种能让改变的格子不超过 \(\frac{1}{2}\)(由于XX
和OO
的贡献是 \(1\);XO
和OX
对某一个贡献是 \(2\),对另外一个没有贡献),因此改变的总格子数不超过 \(\lfloor\frac{k}{3}\rfloor\)spa
实现的时候讨论每种修改方案,看哪一种知足条件便可。code
总结get
限制出如今相邻格子上,染色是很好的解决方案。string
#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(); }
题目描述it
点此看题io
解法class
首先考虑没有问号的状况怎么计算答案,设 \(b_o,b_e\) 分别表示奇数位置和偶数位置上 \(b\) 颜色的个数,\(w_o,w_e\) 相似,其实贪心均可以猜出结论:\(f(c)=\frac{1}{2}|b_o-b_e|\),设 \(|b_o-b_e|=2k\),证实:
这个结论能够用于计数,设 \(F\) 为?
的个数,\(F_o\) 为偶数位置?
的个数,咱们枚举?
中有 \(i\) 个位置,若是是偶数位置染色为 \(b\),不然染色成 \(w\),其余位置用相反的方法染色。设 \(i\) 中染色 \(b\) 的个数为 \(a\),那么 \(b\) 颜色在偶数位置的个数是 \(b_e+a\),在奇数位置的个数是 \(b_o+F_o-i+a\)(由于 \(i-a\) 是奇数位置用掉的?
个数),因此:
设 \(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}\) 种,因此答案是:
从 \(\tt easy\space version\) 继续,暂且忽略 \(\frac{1}{2^F}\) 这个系数,咱们把绝对值拆掉(默认 \(i=x\bmod 2\)):
首先咱们把组合数前面的系数拿掉,对于 \(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}\) 能够直接转成前缀和的形式,组合数前缀和是很容易修改的,只须要使用这个恒等式:
总结
通常这种题都有结论来支持计数,要大胆猜结论。
推式子的时候注意绝对值能够拆掉,若是式子的主体是组合数,能够尝试把他变成组合数前缀和的形式。
#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); }