zoukankan      html  css  js  c++  java
  • [HNOI2011]卡农

    题目

    省队集训的时候遇到一个加强版,推了很久的集合容斥之后自闭了

    考虑递推,设(f_i)表示选择(i)([1,2^n-1])里的数,且异或和为(0)的排列数,答案就是(frac{f[m]}{m!})

    考虑转移,发现不好转移,于是考虑在转移的时候正难则反一下

    我们现在需要求(f_i),由于现在的异或和是(0),那么前(i-1)个异或起来一定不是(0),前(i-1)个有(A_{2^n-1}^{i-1})种方案,减掉异或和为(0)(f_{i-1})种,那么剩下的异或和肯定不是(0)

    现在直接选择一个当前的异或和使得当前的异或和为(0)就好了,但是这样可能就会出现最后选择的这个数在之前已经出现过了,于是我们还要消去一些东西

    考虑连续选择两个相同的数异或和不变,于是考虑从(f_{i-2})转移

    我们直接从(f_{i-2})的基础上选择两个一样的数,这样的选择有(2^n-1-(i-2))种,其中一个需要在最后一个,另外的一个需要插到前面的(i-2)个数里,也就是(i-1)个空位中

    于是(f_i=A_{2^n-1}^{i-1}-f_{i-1}-(i-1)(2^n-i+1)f_{i-2})

    代码

    #include <bits/stdc++.h>
    #define re register
    const int mod = 100000007;
    int n, m, now, k, pw, fac;
    int f[1000005];
    inline int ksm(int a, int b) {
        int S = 1;
        for (; b; b >>= 1, a = 1ll * a * a % mod)
            if (b & 1)
                S = 1ll * S * a % mod;
        return S;
    }
    int main() {
        scanf("%d%d", &n, &m);
        pw = 1;
        f[1] = 0, f[0] = 1;
        pw = ksm(2, n) - 1;
        now = pw, k = pw, fac = 1;
        for (re int i = 2; i <= m; i++) {
            f[i] =
                ((now - f[i - 1] + mod) % mod - 1ll * f[i - 2] * (i - 1) % mod * (k - i + 2) % mod + mod) % mod;
            pw--;
            now = 1ll * now * pw % mod;
            fac = 1ll * fac * i % mod;
        }
        printf("%d
    ", 1ll * f[m] * ksm(fac, mod - 2) % mod);
        return 0;
    }
    
  • 相关阅读:
    统计SQL
    记一次惨痛教训
    window ssh 连接 本地虚拟机ubuntu 16
    js undefined 笔记
    java new java.text.SimpleDateFormat("yyyyMM01").format(date)
    java 反射
    mapinfo使用
    EnableAutoConfiguration注解说明
    Eureka Server 代码分析01
    BigDecimal 学习比较
  • 原文地址:https://www.cnblogs.com/asuldb/p/11189772.html
Copyright © 2011-2022 走看看