AtCoder Grand Contest 043 部分题解

警告,这里只有 \(AB\) 题解,努力一下也许能再做个一题

这场打得好爽,rank \(299\),涨了 \(141\)

AGC043A

乍一看有点不知所措。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;
}

AGC043B

又是一道第一眼看没有思路的题。

这道题让我想起了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;
}
posted @ 2020-03-22 09:27  beacon_cwk  阅读(563)  评论(2编辑  收藏  举报