容斥原理练习记录

HDU 1796 How many integers can you find

题意

给你一个集合n,里面有1–n-1的整数,再给你一个集合m,里面有m个非负整数。
求集合n中能被集合m中某个数整除的数的个数。

很明显就是枚举因子的容斥,但是注意如果有两个因子6和9,那不能用他们相乘来容斥,应该取LCM。
正常情况下,应该是找素因子来容斥的。
这题m集合中可能会有0,要特判。


HDU4135 Co-prime

#题意
给一个区间[a,b]和整数n,问区间a,b内有多少数和n互质
#解
容斥删掉区间内不互质即可。
先质因数分解,求出所有素因子,注意一个素因子就算出现两次也只需要放入一个进数组就可以,比如20 = 225,只要2,5就可以
然后求[1,b]内互质的数,在求[1,a-1]互质的数,相减即可。


HDU2204 - Eddy’s爱好

题意

给你一个数n,问在1-n之中有多少个数能表示成 m^k 且k大于1。(注意1可以表示成1 = 1^2 等等,所以1也算)。

想到的是求$ n^{\frac{1}{2}} 表 示 二 次 方 &lt; = n , 表示二次方&lt;=n, <=nn ^ \frac{1}{3} 表 示 三 次 方 &lt; = n , 表示三次方&lt;=n , <=nn ^ \frac{1}{4}$表示四次方<=n这样,但是这里显然是有重叠的比如2^6 = 4^3 = 8^2,所以和上面题一样,枚举素因子2,3,5,7,让他们作为分母来容斥

然后这里我本来是不想用pow的,但是如果用二分来求的话,很难估计上界,一个弄不好就爆long long。

就试了一下pow加了1e-7做修正,结果也能过。。

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
ll n;
vector<int> v;

int main()
{
    vector<int> v={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59};
    int sz = v.size();
    while (scanf("%lld",&n) != EOF)
    {
        ll ans = 0;
        for (int i=1;i<(1<<sz);i++)
        {
            int cnt = 0;
            int jie = 1;
            bool flag=true;
            for (int j=0;j<sz;j++)
                if ( i & (1<<j) )
                {
                    jie = jie * v[j],cnt++;
                    if (jie > 61)
                    {
                        flag = false;
                        break;
                    }
                }
            if (!flag) continue;
            ll tmp = pow(n,1.0/jie) + 1e-7;
            if (cnt & 1) ans += max(tmp-1,0ll);
            else ans -= max(tmp-1,0ll);
        }
        printf("%lld\n",ans+1);
    }
    return 0;
}


CF- Gym - 100548F

题意

给你n多花,m种颜料,让你选择k种颜料,要求是必须用这k种颜料都用到,并且相邻花颜色不能相同,给n朵花上色。问结果又多少种情况?

首先设最多用k种颜料的情况(不保证k种全用到),那么有$ k * (k -1)^{n-1} $ ,也就是第一朵花k种情况,后面的都是k-1种情况。
然后再看最多用k-1种颜料的情况,那就是$ C_k^{k-1} * (k-1) * (k-2)^{n-1} $,也就是从k种中间选择k-1种,然后就是和上面一样考虑。
仔细想想会发现,最多用k种的时候,包含了最多用k-1和k-2等等直到最多2种。
而最多用k-1种里面包含了最多用k-1种直到最多2种。那么就可以容斥了。
公式就是 $ C_m^k*( k * (k-1)^{n-1} + \Sigma_{i=1}^{k-2} (-1)^i * (k-i) * (k-i-1)^{n-1} * C_k^{k-i} ) $
然后这题需要处理阶乘的逆元


HDU-1695 GCD

#题意
给你两个区间1-b和1-d,各挑一个数,问他们的GCD是k的情况有多少种,(a1,a2) 和(a2,a1)视为一种情况。

#解
首先那两个数都除k的话就互质,所以可以先把b,d都除k,然后就是两个区间内找互质的数。
我一开始想的是遍历对某个区间的所有数,对每个数都在另一个区间找互质的数,可以用容斥做,但是这样显然会重复计算,而且超时。
找互质数的个数,并且还不重复的话,欧拉函数就可以,因为他是求小于它和它互质的数。所以先求一个右区间的最小值比如说是b,对这个范围内的欧拉函数求和。然后剩下的就是求(b+1,d)和(1-b)互质的数,这时候就可以用一开始容斥来做,因为 &gt; = b + 1 &gt;=b+1 >=b+1的数只能在第一个区间选到,所以不会重复。

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int MAXN = 1e5+100;
int a,b,c,d,k,low,up,t;
int euler[MAXN];
ll ans;
vector<int> v;

void init()
{
    euler[1] = 1;
    for (int i=2;i<MAXN;i++)
    {
        if (!euler[i])
            for (int j=i;j<MAXN;j+=i)
        {
            if (!euler[j]) euler[j]=j;
            euler[j] = euler[j] / i * (i-1);
        }
    }
}

void getfactor(int num)
{
    v.clear();
    for (int i=2;i<=num/i;i++)
    {
        if ( num % i == 0 )
        {
            v.push_back(i);
            while (num % i == 0) num /= i;
        }
    }
    if (num > 1) v.push_back(num);
}

ll gethuzhi(int up)
{
    ll ans = 0;
    int sz = v.size();
    for (int i=1;i<(1ll << sz);i++)
    {
        int cnt = 0;
        int tmp = 1;
        for (int j=0;j<sz;j++)
        {
            if ( i & (1ll << j) )
            {
                cnt ++;
                tmp = tmp * v[j];
            }
        }
        if (cnt & 1) ans += low / tmp;
        else ans -= low / tmp;
    }
    return low - ans;
}

int main()
{
    init();
    scanf("%d",&t);
    for (int tt=1;tt<=t;tt++)
    {
        ans = 0;
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
        if ( k == 0 )
        {
            printf("Case %d: 0\n",tt);
            continue;
        }
        b /= k;
        d /= k;
        low = min(b,d);
        up = max(b,d);
        long long ans = 0;
        for (int i=1;i<=low;i++) ans += euler[i];
        for (int i=low+1;i<=up;i++)
        {
            getfactor(i);
            ans += gethuzhi(i);
        }
        printf("Case %d: %lld\n",tt,ans);
    }
    return 0;
}


#HDU2841 Visible Trees
#解
其实和上面一题是一样的,不过这里反过来也记一次。同样也是先求都包含的区间,欧拉函数求和,然后$ *2-1$,因为(1,1)实际上只有一个,所以要减掉。
下一步就是对(b+1,d)求(1,b)里面和它互质的个数。
要注意的是别用vector了,反复清空很慢。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值