Codeforces-Round-940-Div-2

注意: \color{red}{注意:} 注意: 个人博客传送门

A. Stickogon

思路:

  • 题目要求每条边只能有一个木棍,然后我们可以联想到最小的正多边形是正三角形,所以只需要将每种木棍的个数存起来,然后求可以组成多少个正三角形即可

时间复杂度: O ( n ) O(n) O(n)

void solve() {
    int n;
    std::cin >> n;

    std::vector<int> a(110);
    for (int i = 0; i < n; i++) {
    	int x;
    	std::cin >> x;
    	a[x]++;
    }

    int ans = 0;
    for (int i = 1; i <= 100; i++) {
    	ans += a[i] / 3;
    	a[i] %= 3;
    }
    std::cout << ans << "\n";
}

B. A BIT of a Construction

思路:

  • 有一个关于位运算的前置知识,将一个int范围里的每一位都置为1可以这样操作(1 << 31) - 1,那么我们将一个小于x的最大数的每一位置为1就是(1 << __lg(x)) - 1,所以我们只需要把小于等于k的最大那个二进制下1最多的那个数求出来即可,然后输出k - xn - 20

时间复杂度: O ( n ) O(n) O(n)

void solve() {
    int n, k;
    std::cin >> n >> k;

    if (n == 1) {
        std::cout << k << "\n";
    } else {
        int b = std::__lg(k);
        std::cout << (1 << b) - 1 << " " << k - (1 << b) + 1 << " ";
        for (int i = 3; i <= n; i++) {
            std::cout << 0 << " ";
        }
        std::cout << "\n";
    }
}

C. How Does the Rook Move?

题目大意:每次玩家可以选择一个位置 ( r i , c i ) (r_i, c_i) (ri,ci) 然后机器人会在镜像位置 ( c i , r i ) (c_i, r_i) (ci,ri) 也放一个棋子,如果玩家在对角线上放一个棋子 ( i , i ) (i, i) (i,i) ,那么机器人不会操作,然后题目要求给你一个残局,问有多少种满足 N N N 皇后问题的解 → \rightarrow 每行每列都只存在一个棋子

思路:

  • 我们可以发现,每当我们在非对角线的位置 ( r i , c i ) (r_i, c_i) (ri,ci) 下一个任意颜色的棋子时,机器人在镜像位置 ( c i , r i ) (c_i, r_i) (ci,ri) 也会下一个棋子,那么我们将丢失2行和2列,如果在对角线位置下一个棋子,我们只会丢失11列。
  • 我们用分治的思想考虑,每次下一个棋子,这一个或者两个棋子,会把棋盘分割为1个或者4个小的棋盘,棋盘的大小不重要,重要的在于,大的棋盘会变成更小的棋盘,而且被分割出的棋盘一定是一个边长为k的正方形,所以问题就变成了求一个边长为k的棋盘上的n皇后问题。
    • 对于一个边长为1的正方形棋盘,它的方案数为1,一个边长为2的棋盘,它的方案数为3,我们可以发现,一个边长为x的棋盘,它的方案数由一个边长为x - 1的棋盘继承而来。
    • 对于我们下一步棋,我们如果下在对角线,那么会损失1行和1列,会产生一个k - 1阶的新棋盘,然后在这个新的棋盘中,其方案数为 1 × d p [ k − 1 ] 1 \times dp[k - 1] 1×dp[k1]
    • 如果我不下在对角线处,就会损失22列,那么我们有 2 × ( k − 1 ) 2 \times (k - 1) 2×(k1) 种下棋方案,剩下k - 2阶的棋盘,那么在一个k - 2阶的棋盘里,它的方案数为 2 × ( k − 1 ) × d p [ k − 2 ] 2 \times (k - 1) \times dp[k - 2] 2×(k1)×dp[k2]
  • 所以,对于一个N阶的棋盘,它的方案数为 d p [ k ] = d p [ k − 1 ] + 2 × ( k − 1 ) × d p [ k − 2 ] dp[k] = dp[k - 1] + 2 \times (k - 1) \times dp[k - 2] dp[k]=dp[k1]+2×(k1)×dp[k2]

时间复杂度: O ( N ) O(N) O(N)

如果实在不理解,可以看看这个图文讲解

constexpr int N = 3E5 + 10, P = 1E9 + 7;
i64 dp[N];

void solve() {
    int n, k;
    std::cin >> n >> k;

    for (int i = 0; i < k; i++) {
        int r, c;
        std::cin >> r >> c;

        if (r == c) {
            n -= 1;
        } else {
            n -= 2;
        }
    }
    std::cout << dp[n] << "\n";
}

signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    dp[0] = dp[1] = 1;
    for (int i = 2; i <= N; i++) {
        dp[i] = dp[i - 1] + (dp[i - 2] * 2) % P * (i - 1) % P;
        dp[i] %= P;
    }

    int t;
    std::cin >> t;

    while (t--) {
        solve();
    }    

    return 0;
}

D. A BIT of an Inequality

思路:

  • 可以把题目的式子变形一下 f ( x , y )   ⊕   f ( y , z ) > f ( x , z ) f(x, y) \space \oplus \space f(y, z) > f(x, z) f(x,y)  f(y,z)>f(x,z)
  • 根据题目给的 n n n 的范围( 1 0 5 10^5 105),这题就一定不是枚举,不然就是 O ( n 3 ) O(n^3) O(n3),铁定超时。要做到 O ( 1 ) O(1) O(1) 的时间查询,那么我们可以采用异或前缀和
  • 我们根据异或前缀和的思路,把式子变形一下 f ( x , y )   ⊕   f ( y , z ) > f ( x , z ) ⇒ ( S z − S x − 1 )   ⊕   a y > ( S z − S x − 1 ) f(x, y) \space \oplus \space f(y, z) > f(x, z) \Rightarrow (S_z - S_{x - 1}) \space \oplus \space a_y > (S_z - S_{x - 1}) f(x,y)  f(y,z)>f(x,z)(SzSx1)  ay>(SzSx1),那么我们可以明确的知道,这个式子左边多异或的那个 a y a_y ay 它可以使得整个式子变大,然后我们考虑,在一个数什么情况下,它异或另一个数,可以使得结果变大,一定是这个 a y a_y ay 的最高位是 1 1 1,然后我们可以按这个思路,设数组 b i t [ i ] [ j ] bit[i][j] bit[i][j] 表示第 i i i 位前缀和 1 1 1 的个数,利用乘法原理,进行计算有多少个三元组满足条件。

时间复杂度: O ( 30 × n ) O(30 \times n) O(30×n)

void solve() {
    int n;
    std::cin >> n;

    std::vector<int> a(n + 1);
    for (int i = 1; i <= n; i++) {
        std::cin >> a[i];
    }

    std::vector<i64> s(n + 1);
    std::vector<std::vector<i64>> bit(30, std::vector<i64>(n + 1));
    for (int i = 1; i <= n; i++) {
        s[i] = s[i - 1] ^ a[i];
        for (int j = 0; j < 30; j++) { // check the kth bit
            bit[j][i] = bit[j][i - 1] + (s[i] >> j & 1);
        }
    }

    i64 ans = 0;
    for (int i = 1; i <= n; i++) {
        int k = 29;
        while (k >= 0 && (a[i] >> k & 1) == 0) k--; // find the highest bit of 1
        if (k == -1) continue ; // if x is 0 
        ans += (bit[k][n] - bit[k][i - 1]) * bit[k][i - 1]; // the number of 1
        ans += (n - i + 1 - (bit[k][n] - bit[k][i - 1])) * (i - bit[k][i - 1]); // the number of 0
    }
    std::cout << ans << "\n";
}
  • 17
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值