警告,這里只有 \(AB\) 題解,努力一下也許能再做個一題
這場打得好爽,rank \(299\),漲了 \(141\)
乍一看有點不知所措。BFS?暴力?
讓我們冷靜分析一下。要達成目標,必須有至少一條從左上到右下的路徑。
感受一下:
xxx..
..x..
..xx.
...x.
...xx
注意到操作是同時對一個矩形區域操作。不難發現:這樣可以對路徑上任意一段連續序列取反。
怎樣操作最優呢?
根據首尾,可以分為四種情況:
#.#.# (答案:3)
.#.#. (答案:2)
#.#. (答案:2)
.#.# (答案:2)
綜上,答案就是路徑中連續的"#"的數量。
\(dp\) 就可以了。
我自己測的時候把“#”打成"X",懵逼了兩分鍾
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mit map<int,int>::iterator
#define sit set<int>::iterator
#define itrm(g,x) for(mit g=x.begin();g!=x.end();g++)
#define itrs(g,x) for(sit g=x.begin();g!=x.end();g++)
#define ltype int
#define rep(i,j,k) for(ltype(i)=(j);(i)<=(k);(i)++)
#define rap(i,j,k) for(ltype(i)=(j);(i)<(k);(i)++)
#define per(i,j,k) for(ltype(i)=(j);(i)>=(k);(i)--)
#define pii pair<int,int>
#define fi first
#define se second
#define mpr make_pair
#define pb push_back
#define fastio ios::sync_with_stdio(false)
const int inf=0x3f3f3f3f,mod=1000000007;
const double pi=3.1415926535897932,eps=1e-6;
void chmax(int &x,int y){if(x < y) x = y;}
void chmin(int &x,int y){if(x > y) x = y;}
int n,m,dp[105][105];char s[105][105];
int main()
{
scanf("%d%d",&n,&m);
rep(i,1,n) scanf("%s",s[i]+1);
dp[1][1] = (s[1][1] == '#');
rep(i,1,n) rep(j,1,m)
if(i != 1 || j != 1){
dp[i][j] = inf;
char cur = s[i][j];
if(i > 1) {
char prev = s[i-1][j];
if(prev == cur) chmin(dp[i][j], dp[i-1][j]);
else chmin(dp[i][j], dp[i-1][j] + (cur == '#'));
}
if(j > 1) {
char prev = s[i][j-1];
if(prev == cur) chmin(dp[i][j], dp[i][j-1]);
else chmin(dp[i][j], dp[i][j-1] + (cur == '#'));
}
}
printf("%d\n",dp[n][m]);
return 0;
}
又是一道第一眼看沒有思路的題。
這道題讓我想起了agc006d。其實這兩題做法完全不同。
我們不考慮 \(O(n^2)\)。
首先我們發現,把原數列每個數減少 \(1\) 對答案沒有任何影響。
然后,先看只有 \(0\) 和 \(2\) 的情況。這樣答案要么是 \(0\) ,要么是 \(2\)
我比賽時先考慮反推:最后的結果是 \(2\) ,原來可能的序列有:
0 2
2 2 0
0 2 0 0
2 2 0 0 0
這個做法好像沒什么前途。
換個思路,正向思考,我們假設只有一個 \(2\) ,兩邊有足夠的 \(0\) ,它會這樣發展:
0 0 0 0 0 0 2 0 0 0 0 0 0
0 0 0 0 0 2 2 0 0 0 0 0
0 0 0 0 2 0 2 0 0 0 0
0 0 0 2 2 2 2 0 0 0
0 0 2 0 0 0 2 0 0
0 2 2 0 0 2 2 0
2 0 2 0 2 0 2
2 2 2 2 2 2
是不是有點眼熟?它就和組合數模 \(2\) 的圖案一樣!
有些圖案在外面被忽略了,但是顯示出來的一定不會受到影響。因為已經在外面的,怎樣向左下或右下走,都不能走回去。
所以算組合數模 \(2\) (也就是這個組合數的奇偶性)就行了!
如果第一行有多個 \(2\) 呢?它們會互相疊加,偶數個疊加在一起會消除。所以,先得出對答案有影響的位置數 \(num\) ,如果 \(num % 2\) 為 \(0\) ,答案為 \(0\),否則為 \(2\)。
只有 \(0\) 和 \(1\) 的情況同理。只有 \(1\) 和 \(2\) 的情況,把每個數減去 \(1\) ,也變成的左邊的那種情況。
如果 \(0,1,2\) 都有呢?
答案不能超過 \(1\) ,因為序列中的 \(1\) 會持續造成影響,\(|0-1| = |2-1| = 1\),即 \(1\) 不會完全被消掉。在倒數第 \(2\) 輪的時候如果至少有一個數非零,其中一定有一個數是 \(1\)。
又由於 \(|0-1| = |2-1| = 1\),\(0\) 和 \(2\) 這時是等價的,可以直接把 \(2\) 看成 \(0\)。
細節方面,如何算出 \(C(a,b)\) 的奇偶性?
令 \(fac(i)\) 表示 \(i!\) 中約數 \(2\) 的個數。計算組合時把相除改成相減,若計算出的值為 \(0\) ,則 \(C(a,b)\) 為奇數。
最后記得特判所有數字相等的情況。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mit map<int,int>::iterator
#define sit set<int>::iterator
#define itrm(g,x) for(mit g=x.begin();g!=x.end();g++)
#define itrs(g,x) for(sit g=x.begin();g!=x.end();g++)
#define ltype int
#define rep(i,j,k) for(ltype(i)=(j);(i)<=(k);(i)++)
#define rap(i,j,k) for(ltype(i)=(j);(i)<(k);(i)++)
#define per(i,j,k) for(ltype(i)=(j);(i)>=(k);(i)--)
#define pii pair<int,int>
#define fi first
#define se second
#define mpr make_pair
#define pb push_back
#define fastio ios::sync_with_stdio(false)
const int inf=0x3f3f3f3f,mod=1000000007;
const double pi=3.1415926535897932,eps=1e-6;
void chmax(int &x,int y){if(x < y) x = y;}
void chmin(int &x,int y){if(x > y) x = y;}
int n,a[1000005],fac[1000005],ans,cnt[4],mup=1;char s[1000005];
int C(int x,int y){
return fac[x] - fac[y] - fac[x - y];
}
int main()
{
scanf("%d%s",&n,s+1);
rep(i,1,n) a[i] = s[i] - 49, cnt[a[i]]++;
rep(i,1,n){
fac[i] = fac[i-1];
int ii = i;
while(ii % 2 == 0) {
ii /= 2;
fac[i]++;
}
}
int pp = (cnt[0]>0) + (cnt[1]>0) + (cnt[2]>0);
if(pp <= 1){puts("0");return 0;}
if(cnt[0] && cnt[1] && cnt[2]){
rep(i,1,n) if(a[i] == 2) a[i] = 0;
}
else if(cnt[0] && cnt[2]){
mup = 2;
}
else if(cnt[1] && cnt[2]){
rep(i,1,n) a[i]--;
}
rep(i,1,n) if(a[i]) {
ans ^= (C(n - 1,i - 1) == 0);
}
printf("%d\n",ans * mup);
return 0;
}