題目描述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); }