codeforces 439 E. Devu and Birthday Celebration 组合数学 容斥定理

题意:

q个询问,每一个询问给出2个数sum,n

1 <= q <= 10^5, 1 <= n <= sum <= 10^5

对于每一个询问,求满足下列条件的数组的方案数

1.数组有n个元素,ai >= 1

2.sigma(ai) = sum

3.gcd(ai) = 1

solution:

这道题的做法类似bzoj2005能量采集

f(d) 表示gcd(ai) = d 的方案数

h(d) 表示d|gcd(ai)的方案数

令ai = bi * d

则有sigma(bi) = sum / n

  d | gcd(ai)

还要满足bi >= 1

则显然有h(d) = C(sum / d - 1,n - 1)

    h(d) = f(d) + f(2d) + ... + f(d_max)

这里的d满足:

1.d是sum 的约数

2.sum / d >= n

则f(d) = h(d) - sigma(f(j)) ,2d <=j<=sum/n

倒序遍历d

ans = f(1)

由于询问的次数太多,每次询问后,可以把(sum,n)放入map中,记录下来

  //File Name: cf439E.cpp
  //Author: long
  //Mail: [email protected]
  //Created Time: 2016年02月17日 星期三 14时58分16秒

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <cmath>
#include <cstdlib>
#include <vector>

#define LL long long
#define pb push_back

using namespace std;

const int MAXN = 1e5+5;
const int MOD = 1e9+7;

LL f[MAXN];
LL jie[MAXN];
bool is[MAXN];
vector<int> dive;
map< pair<int,int>,int > rem; 

void init()
{
    jie[0] = 1;
    for(int i=1;i<MAXN;i++){
        jie[i] = jie[i-1] * i % MOD;
    }
    rem.clear();
}

void get_dive(int sum,int n)
{
    int e = (int)sqrt(sum + 0.0);
    dive.clear();
    int j;
    for(int i=1;i<=e;i++){
        if(sum % i == 0){
            if(sum / i >= n)
                dive.pb(i);
            j = sum / i;
            if(j != i && sum / j >= n)
                dive.pb(j);
        }
    }
    sort(dive.begin(),dive.end());
    for(int i=0;i<dive.size();i++){
        is[dive[i]] = true;
    }
}

LL qp(LL x,LL y)
{
    LL res = 1LL;
    while(y){
        if(y & 1)
            res = res * x % MOD;
        x = x * x % MOD;
        y >>= 1;
    }
    return res;
}

LL comb(int x ,int y)
{
    if(y < 0 || y > x)
        return 0;
    if(y == 0 || y == x)
        return 1;
    return jie[x] * qp(jie[y] * jie[x-y] % MOD,MOD  - 2) % MOD;
}

void solve(int sum,int n)
{
    map< pair<int,int>,int >::iterator it;
    it = rem.find(make_pair(sum,n));
    if(it != rem.end()){
        printf("%d\n",(int)(it->second));
        return ;
    }
    memset(f,0,sizeof f);
    memset(is,false,sizeof is);
    get_dive(sum,n);
    int ma = dive.size();
    for(int i=ma-1;i>=0;i--){
        int d = dive[i];
        f[d] = comb(sum / d - 1,n - 1);
        for(int j=2*d;j<=dive[ma-1];j+=d){
            if(is[j]){
                f[d] = ((f[d] - f[j] + MOD) % MOD + MOD) % MOD;
            }
        }
    }
    printf("%d\n",(int)f[1]);
    rem[make_pair(sum,n)] = f[1];
    return ;
}

int main()
{
    init();
    int test;
    scanf("%d",&test);
    while(test--){
        int sum,n;
        scanf("%d %d",&sum,&n);
        solve(sum,n);
    }
    return 0;
}
时间: 2024-07-31 02:47:13

codeforces 439 E. Devu and Birthday Celebration 组合数学 容斥定理的相关文章

cf451E Devu and Flowers 卢卡斯定理+容斥定理

题目:http://codeforces.com/problemset/problem/451/E 题意:有n个盒子(n<=20),每个盒子中有10^12个小球,现从每个盒子中取出若干球(可为0),求共取出s个小球(s<=10^14)的方案数. 组合数学问题,求C(n,m).但n,m过大时,可用卢卡斯定理. 卢卡斯定理:C(n,m) %p = C(n/p,m/p) * C(n%p,m%p) 从n个盒子中取出s个球的方案数,相当于插板,即 C(s+n-1,n-1).注意这是没有限制条件的情况.

Codeforces 451 E. Devu and Flowers(组合数学,数论,容斥原理)

传送门 解题思路: 假如只有 s 束花束并且不考虑 f ,那么根据隔板法的可重复的情况时,这里的答案就是 假如说只有一个 f 受到限制,其不合法时一定是取了超过 f 的花束 那么根据组合数,我们仍然可以算出其不合法的解共有: 最后,由于根据容斥,减两遍的东西要加回来,那么含有偶数个 f 的项为正,奇数个时为负. 答案就是: 搜索答案,使用Lucas定理,计算组合数上下约去. 代码: 1 #include<cstdio> 2 #include<cstring> 3 #include&

HDU 5155 Harry And Magic Box(组合数学+容斥)

Problem Description One day, Harry got a magical box. The box is made of n*m grids. There are sparking jewel in some grids. But the top and bottom of the box is locked by amazing magic, so Harry can't see the inside from the top or bottom. However, f

codeforces 340E Iahub and Permutations(错排or容斥)

转载请注明出处: http://www.cnblogs.com/fraud/          ——by fraud Iahub and Permutations Iahub is so happy about inventing bubble sort graphs that he's staying all day long at the office and writing permutations. Iahubina is angry that she is no more import

Codeforces 838A - Binary Blocks(二维前缀和+容斥)

838A - Binary Blocks 思路:求一下前缀和,然后就能很快算出每一小正方块中1的个数了,0的个数等于k*k减去1的个数,两个的最小值就是要加进答案的值. 代码: #include<bits/stdc++.h> using namespace std; #define ll long long #define pb push_back #define mem(a,b) memset((a),(b),sizeof(a)) const int INF=0x3f3f3f3f; cons

UVA 11806 组合数学+容斥

UVA: https://vjudge.net/problem/UVA-11806 题意:给你一个n×mn×m的矩阵网格和kk个人,问有多少种方法使得每一个格子只放一个人,并且第一行,最后一行,第一列,最后一列都有人.直接枚举似乎有难度,我们考虑容斥.rr行cc列放kk个人的方案数是(r×ck)(r×ck),那么由容斥原理,总方案为 ans=U?A?B?C?D+AB+AC+AD+-+ABCDans=U?A?B?C?D+AB+AC+AD+-+ABCD 其中UU表示没有限制的方案数,A,B,C,DA

HDU - 6513 Reverse It (SYSU校赛C题)(组合数学+容斥)

题目链接 题意:给定一个n*m的矩阵,可以选择至多两个子矩阵将其反转,求能形成多少种不同的矩阵. 任选一个矩阵有$C_{n+1}^{2}C_{m+1}^{2}$种方法,任选两个不同的矩阵有$C_{C_{n+1}^{2}C_{m+1}^{2}}^{2}$种方法,但其中有重复的,需要去重. 重复的情况一共有以下四种: 第一种,两矩阵拼合成一个矩阵,这样的图形有$C_{n+1}^{2}C_{m+1}^{2}$个,重复度(出现的次数)为(n+m-2) 第二种,形成的两个矩阵在同一行或同一列,有$C_{n

Codeforces Round #589 (Div. 2)-E. Another Filling the Grid-容斥定理

Codeforces Round #589 (Div. 2)-E. Another Filling the Grid-容斥定理 [Problem Description] 在\(n\times n\)的格子中填入\([1,k]\)之间的数字,并且保证每一行至少有一个\(1\),每一列至少有一个\(1\),问有多少种满足条件的填充方案. [Solution] 令\(R[i]\)表示为第\(i\)行至少有一个\(1\)的方案数,\(C[i]\)表示第\(i\)列至少有一个\(1\)的方案数.则题目要

Codeforces 439E Devu and Birthday Celebration(计数问题)

题目链接:Codeforces 439E Devu and Birthday Celebration 题目大意:给出q,表示询问的次数,每次询问有n和f,问有多少种分类方法,将n分成f份,并且这f份的最大共约数为1. 解题思路:如果不考虑说最大共约数为1的话,那么问题很简单,就是f个数的和为n的种数C(f?1n?1).所以我们就尽量将问题转化成说f数的和为s的子问题.用容斥原理,总的可能减去公约数不为1的情况,那么公约数不为1的情况就去枚举公约数. #include <cstdio> #inc