• [BZOJ2339][HNOI2011]卡农


    [BZOJ2339][HNOI2011]卡农

    试题描述

    输入

    见“试题描述

    输出

    见“试题描述

    输入示例

    见“试题描述

    输出示例

    见“试题描述

    数据规模及约定

    见“试题描述

    题解

    先考虑 m 个 01 串排顺序的情况。可以发现如果定下前 m - 1 个 01 串,那么第 m 个串就可以由前面所有 01 串按位异或得出,所以方案数为 A(2n - 1, m - 1)(即除全 0 串外的所有情况选择 m - 1 个并排列顺序的方案数),现在我们需要减去不合法的情况。我们令 f(m) 表示前 m 个串考虑顺序合法的方案数。

    不合法#1:所有 m - 1 个 01 串的异或和为全 0 串,即最后一个填的是全 0 串,那么要减去 f(m - 1)。

    不合法#2:最后一个填的串与前 m - 1 个串有重复,那么如果去掉这两个相同的串,剩下的串能组成合法的情况(即 f(m - 2)),然后这个串可能与前 m - 1 个串中任意一个串重复,所以要乘上 m - 1,最后这个重复的串本身有 2n - 1 - (m - 2) 种情况。所以最终需要减去 f(m - 2) * (m - 1) * [2n - 1 - (m - 2)]。

    最后再把答案除以 m! 就好了,求一下逆元。

    注意不可以用组合数的方法避免排顺序,因为如果这样做了,在“不合法#2”这一步中我们就不能保证对于那 m - 1 个重复的位置每一个都是完整的 f(m - 2) 中情况,可以想象一下如果用组合数,那么方案中的 01 串就按照一固定顺序排好了,那么对于一个特定的串它的位置就没有任意性了。

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <stack>
    #include <vector>
    #include <queue>
    #include <cstring>
    #include <string>
    #include <map>
    #include <set>
    using namespace std;
    
    int read() {
        int x = 0, f = 1; char c = getchar();
        while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
        while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
        return x * f;
    }
    
    #define maxn 1000010
    #define MOD 100000007
    #define LL long long
    int f[maxn], A[maxn];
    
    void gcd(LL a, LL b, LL& x, LL& y) {
    	if(!b){ x = 1; y = 0; return ; }
    	gcd(b, a % b, y, x); y -= a / b * x;
    	return ;
    }
    LL Inv(LL a) {
    	LL x, y;
    	gcd(a, MOD, x, y);
    	x = (x % MOD + MOD) % MOD;
    	return (x % MOD + MOD) % MOD;
    }
    
    int main() {
    	int n = read(), m = read();
    	
    	int sum2 = 1;
    	for(int i = 1; i <= n; i++) {
    		sum2 <<= 1;
    		if(sum2 >= MOD) sum2 -= MOD;
    	}
    	A[0] = 1;
    	for(int i = 1; i <= m; i++) A[i] = (LL)A[i-1] * ((sum2 - i + MOD) % MOD) % MOD;
    	f[1] = f[2] = 0; int tmp = 2;
    	for(int i = 3; i <= m; i++) {
    		f[i] = A[i-1] - ((LL)f[i-2] * (i - 1) % MOD) * ((sum2 - i + 1 + MOD) % MOD) % MOD;
    		if(f[i] < 0) f[i] += MOD;
    		f[i] -= f[i-1];
    		if(f[i] < 0) f[i] += MOD;
    		tmp = (LL)tmp * i % MOD;
    	}
    	printf("%d
    ", (LL)f[m] * Inv(tmp) % MOD);
    	
    	return 0;
    }
    

    BTW,这题不能用 fread 优化读入。。。

  • 相关阅读:
    Android设备管理器
    Android中读取手机联系人
    PopupWindow
    Activity之间数据的传递
    wpa_supplicant
    nandflash之基本特性
    NAND NOR Flash 和MTD
    mtd-util
    linux 常用命令
    minizip -基于zlib开源代码库
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/6286109.html
Copyright © 2020-2023  润新知