容斥原理练习记录

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 = 2*2*5,只要2,5就可以
然后求[1,b]内互质的数,在求[1,a-1]互质的数,相减即可。


HDU2204 - Eddy’s爱好

题意

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

想到的是求 n 1 2 表示二次方<=n, n 1 3 表示三次方<=n , n 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 + Σ 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)互质的数,这时候就可以用一开始容斥来做,因为 >= 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了,反复清空很慢。

猜你喜欢

转载自blog.csdn.net/z631681297/article/details/81318279