codeforces刷题(五)

1、Maximum Subsequence Value

\(n\)个数中挑\(k\)个数,使得\(\sum2^i\)最大(将这\(k\)个数用二进制表示,如果二进制第 \(i\) 位为1的数的个数多于\(max(1,k-2)\)个,就加上\(2^i\))。

题解

性质:选高位为1的数比选多个低位为1的数更优,\(1 + 2^1 + 2^2+···+2^{n-1} <2^n\)

\(n>=3\)时,\(k =3\)能取得最大的\(\sum2^i\)。也就是说,当\(k>3\)时,多选的\(k-3\)个数不能使得原本第\(i\)位的0(\(k =3\)时)变为有效的1,既第\(i\)位全为1,也只有\(k-3\)个数,小于\(max(1,k-2)\)

int main()
{
    int n;
    cin >> n;

    long long a[505];
    myfor(i, 1, n) cin >> a[i];
    
    if (n <= 3) {
        long long ans = a[1];
        myfor(i, 1, n) ans |= a[i];
        cout << ans << endl;
    } 
    else {
        long long ans = 0;
        myfor(i, 1, n) myfor(j, i + 1, n) myfor(k, j + 1, n) ans = max(ans, a[i]|a[j]|a[k]);
        cout << ans << endl;
    }

    return 0;
}

2、Solve The Maze

给一张二维迷宫,出口是\((n,m)\)

  • . 表示这个位置可以行走
  • #表示这个位置是墙
  • G表示这个位置上有一个好人
  • B表示这个位置上有一个坏人

问是否存在一种方案:在某些位置上放墙,既令\(map[i][j] = '\#'\),使得好人走出迷宫而坏人不能走出迷宫。

题解

在坏人的周围都放上墙,然后从出口出发,看是否能走到所有好人所在的位置。


3、Tree Shuffling(好题)

一棵树有\(n\)个节点,每个节点有三个值:\(a\)(花费),\(b\)(已有的值),\(c\)(想要变成的值)

操作:在任意一颗子树中选择\(k\)个节点,然后按照你的意愿排列这\(k\)个节点

操作代价:假设这颗子树的根是\(u\)\(operation\_cost = 2 * k * a[u]\)

问任意次操作后,将所有的节点的\(b\)变成\(c\)的最小代价是多少?

\(b,c\in \{0,1\}\)

题解

贪心,从花费较小的节点开始操作起

思路比较简单,就是不会写

pair<ll, ll> DFS(int u, int pa, int cost)  {
    pair<int, int> now;     // 统计这颗子树需要对几个节点进行重新排列
    if (b[u] != c[u]) {
        now.first += b[u];
        now.second += c[u];
    }
    for (int i = head[u]; i != -1; i = e[i].next) {
        int v = e[i].to;
        if (v == pa) continue;
        pair<ll, ll> nex = DFS(v, u, min(cost, a[v]));
        now.first += nex.first;
        now.second += nex.second;
    }
    int cnt = min(now.first, now.second);
    ans += 2ll * cnt * cost;    // cost: root -> u 这条链上的最小花费
    now.first -= cnt;    
    now.second -= cnt;
    return now;
}

4、Odd-Even Subsequence

给一个序列\(a\),从中挑选\(k\)个数构成序列\(s\)(不改变数的位置)。定义\(x = min(max\{s_1,s_3,s_5,s_7,···\},max\{s_2,s_4,s_6,s_8,···\})\),问最小的\(x\)是多少?

题解

最小化最大值,考虑二分枚举答案,检查以下两种情况即可,换言之,我们需要构造出这样一个子序列\(s\)

  • 答案在奇序列中
  • 答案在偶序列中

我的想法是:假设每次枚举的答案\(mid\)一定在\(a\)中(用\(mid\)构造子序列\(s\)),然后发现\(check\)的时候,非常不好做,因为必须判断假设是否成立。

/*
不断缩小答案的上界
*/
bool check(int x) {
    // 答案在偶序列
    for (int i = 1, cnt = 0; i <= n; i++) {
        if (((cnt & 1) && (a[i] <= x)) || (cnt % 2 == 0)) cnt++;    // 构造合适的s(贪心)
        if (cnt >= k) return true;
    }
    // 答案在奇序列
    for (int i = 1, cnt = 0; i <= n; i++) {
        if (((cnt % 2 == 0) && (a[i] <= x)) || (cnt & 1)) cnt++;
        if (cnt >= k) return true;
    }
    return false;
}

5、AND, OR and square sum

给一个序列\(a\),有如下操作:

  • 任选两个数\(a_i\)\(a_j\),令\(a_i^{'}= a_i \& a_j\)\(a_j^{'} = a_i |a_j\)

可以操作任意次,令\(ans = \sum_{i = 1}^na_i^{'2}\),最大化\(ans\)

题解

假设\(x = a_i |a_j\),有\(x >= a_i\)\(x >= a_j\),所以我们可以让\(a\)中的最大值不断变大,而\(ans\)不会变小。按照这个思路有:令\(a_1 = a_{max}\)(按降序排序),有\(a_1 = a_1 | a_2\)\(a_2 = a_1 \& a_2\)\(a_1 = a_1 | a_3\)\(a_3 = a_1 \& a_3\)、······、\(a_1 = a_1 | a_n\)\(a_n = a_1 \& a_n\)。用相同的办法求出\(a_2^{'}\)\(a_3^{'}\),...,\(a_n^{'}\),既能求得最大的\(ans\)

显然,这样计算需要两次循环,复杂度\(O(n^2)\)。实际上,给出的操作有个性质不会改变每位包含1的个数,既\(a_i^{'} = a_i \& a_j\)\(a_j^{'} = a_i | a_j\)\(a_i\) 的二进制位上的 1 给了 \(a_j​\),但总的1的个数并没有改变。

所以,我们只需要统计二进制每位有多少个1,然后组成一个个数就行了。

妙啊,做\(cf​\)的题始终差那临门一脚!

int main()
{
    int x;
    cin >> n;
    myfor(i, 1, n) {
        cin >> x;
        int t = 0;
        while(x) {
            cnt[t++] += x & 1;
            x >>= 1;
        }
    }

    ll ans = 0;
    myfor(i, 1, n) {
        ll x = 0;
        myfor(j, 0, 20) if (cnt[j]) {
            x += (1 << j);
            cnt[j]--;
        }
        ans += x * x;
    }
    cout << ans << endl;

    return 0;
}
posted @ 2020-06-26 00:13  天之道,利而不害  阅读(138)  评论(0编辑  收藏  举报