HDU 6053 TrickGCD 容斥

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=6053

题意:

给你序列a,让你构造序列b,要求 1<=b[i]<=a[i],且b序列的gcd>=2。问你方案数。

思路:

容易想到的就是我们枚举整个序列的gcd,然后a[i]/gcd就是i位置能够填的数的个数,然后每个位置累积就能得到数列为gcd时的方案数。

最后容斥一下累加就是答案。但是最大gcd可以是100000和明显这样做n^2,会超时。

那么我们把a[i]/gcd的放在一起,然后用快速幂直接求出值。具体来说,当前枚举的gcd是a,把a,2*a,3*a.......分块,对于每一块对gcd为a的贡献是在这一块的a[i]的个数,那么前缀和处理一下就好了。

对于容斥:

也没做过几道容斥,对于dfs的容斥,第一次见,就是说每次容斥的数都是num*prime[i],保证了dfs了所有数,并且只有log层

cnt[num] 是对于num前面已经加或者减了多少次,那么我们只要每个数一次,就对于当前的数需要加或者减1-cnt[num]才能变成1,对于num的倍数也影响了1-cnt[num]。

还有一种容斥,更简单,就是对于gcd倒着遍历,筛去他的倍数的贡献,也就是重复加的要减去。嗯 很巧妙

代码:

代码一:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define MS(a) memset(a,0,sizeof(a))
#define MP make_pair
#define PB push_back
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fLL;
inline ll read(){
    ll x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}
//////////////////////////////////////////////////////////////////////////
const int maxn = 1e5+10;
const int mod = 1e9+7;

int v[maxn],s[maxn],cnt[maxn],res[maxn],mi,mx;
bool vis[maxn];
vector<int> prime;
ll ans;

ll qpow(ll a,ll b){
    ll res = 1;
    while(b){
        if(b&1) res = (res*a)%mod;
        a = (a*a)%mod;
        b >>= 1;
    }
    return res;
}

void init(){
    for(int i=2; i<maxn; i++){
        if(vis[i]) continue;
        prime.push_back(i);
        for(int j=i+i; j<maxn; j+=i){
            vis[j] = true;
        }
    }
}

void dfs(ll num){
    if(num > mi) return ;
    if(cnt[num] == 1) return ;
    if(num > 1){
        int dt = 1-cnt[num];
        ans = (ans+dt*res[num]+mod)%mod;
        cnt[num] = 1;
        for(int i=num*2; i<=mi; i+=num){
            cnt[i] += dt;
        }
    }
    for(int i=0; i<(int)prime.size(); i++)
        dfs(num*prime[i]);
}

int main(){
    init();
    int T = read(), kase = 1;
    while(T--){
        MS(v); MS(s); MS(cnt);
        int n = read();
        mi = INF,mx = 0;
        for(int i=1; i<=n; i++){
            int x = read();
            v[x]++;
            mi = min(mi,x);
            mx = max(mx,x);
        }
        for(int i=1; i<maxn; i++)
            s[i] = s[i-1]+v[i];
        for(int a=2; a<=mi; a++){
            res[a] = 1;
            for(int i=a*2,j=1; i<=mx+a; i+=a, j++){
                int t = (i-1>mx ? s[mx] : s[i-1]);
                res[a] = (res[a]*qpow(j,t-s[i-a-1])%mod);
            }
        }
        // for(int i=2; i<=mi; i++)
        //     cout << i << " " << res[i] << endl;
        ans = 0;
        dfs(1);
        printf("Case #%d: %I64d\n",kase++,ans);
    }

    return 0;
}

代码二:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define MS(a) memset(a,0,sizeof(a))
#define MP make_pair
#define PB push_back
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fLL;
inline ll read(){
    ll x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}
//////////////////////////////////////////////////////////////////////////
const int maxn = 1e5+10;
const int mod = 1e9+7;

int v[maxn],s[maxn],res[maxn],mi,mx;
ll ans,dp[maxn];

ll qpow(ll a,ll b){
    ll res = 1;
    while(b){
        if(b&1) res = (res*a)%mod;
        a = (a*a)%mod;
        b >>= 1;
    }
    return res;
}

int main(){
    int T = read(), kase = 1;
    while(T--){
        MS(v); MS(s); MS(dp);
        int n = read();
        mi = INF,mx = 0;
        for(int i=1; i<=n; i++){
            int x = read();
            v[x]++;
            mi = min(mi,x);
            mx = max(mx,x);
        }
        for(int i=1; i<maxn; i++)
            s[i] = s[i-1]+v[i];
        for(int a=2; a<=mi; a++){
            res[a] = 1;
            for(int i=a*2,j=1; i<=mx+a; i+=a, j++){
                int t = (i-1>mx ? s[mx] : s[i-1]);
                res[a] = (res[a]*qpow(j,t-s[i-a-1])%mod);
            }
        }
        for(int i=mi; i>=2; --i){
            dp[i] = res[i];
            for(int j=i<<1; j<=mi; j+=i){
                dp[i] -= dp[j];
                dp[i] = (dp[i]%mod+mod)%mod;
            }
        }
        ans = 0;
        for(int i=2; i<=mi; i++)
            ans = (ans+dp[i])%mod;
        printf("Case #%d: %I64d\n",kase++,ans);
    }

    return 0;
}
时间: 2024-11-08 13:31:00

HDU 6053 TrickGCD 容斥的相关文章

HDU 6053 - TrickGCD

/* HDU 6053 - TrickGCD [ 莫比乌斯函数,筛法分块 ] 题意: 给出数列 A[N],问满足: 1 <= B[i] <= A[i] ; 对任意(l, r) (1<=l<=r<=n) ,使得 gcd(bl,...br) >= 2 ; 的 B[N] 数列的个数 分析: 设 gcd(b1,...bn) = k (k >= 2),此时 k 对答案的贡献为 (a1/k)*(a2/k)*(a3/k)*...*(an/k) 根据容斥原理,ans = +[k=

hdu 5514 Frogs(容斥)

Frogs Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 1315    Accepted Submission(s): 443 Problem Description There are m stones lying on a circle, and n frogs are jumping over them.The stones a

HDU 5213 分块 容斥

给出n个数,给出m个询问,询问 区间[l,r] [u,v],在两个区间内分别取一个数,两个的和为k的对数数量. $k<=2*N$,$n <= 30000$ 发现可以容斥简化一个询问.一个询问的答案为 $[l,v]+(r,u)-[l,u)-(r,v]$,那么我们离线询问,将一个询问分成四个,分块暴力就行了. 然后就是注意细节,不要发生越界,访问错位置之类比较蠢的问题了. /** @Date : 2017-09-24 19:54:55 * @FileName: HDU 5213 分块 容斥.cpp

HDU 6053 TrickGCD 莫比乌斯函数/容斥/筛法

题意:给出n个数$a[i]$,每个数可以变成不大于它的数,现问所有数的gcd大于1的方案数.其中$(n,a[i]<=1e5)$ 思路:鉴于a[i]不大,可以想到枚举gcd的值.考虑一个$gcd(a_1,a_2,a_3…a_n)=d$,显然每个$a_i$的倍数都满足,有$\frac{a_i}{d}$种方案 那么一个d对答案的贡献为\[\prod_{i=1}^{min(a)}{\lfloor\frac{a_i}{d}\rfloor}    \] 但是所有的d计入会有重复情况,考虑容斥,对d进行素数分

HDU 6053 ( TrickGCD ) 分块+容斥

TrickGCD Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)Total Submission(s): 3401    Accepted Submission(s): 1268 Problem Description You are given an array A , and Zhu wants to know there are how many different

HDU 6053 TrickGCD(分块)

[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=6053 [题目大意] 给出一个数列每个位置可以取到的最大值, 问这个可以构造多少个数列,使得他们的最大公约数大于1 [题解] 我们可以枚举最大公约数k,对于k来说, 他对答案的贡献为∏[ai/k],我们将数列中的数字转化为权值数组 ∏_{i=1}^{100000}[i/k],对于求解i/k的部分我们可以进行数值分块, j*k-1~j*k+k-1的数值除k得到的结果都是相同的,因此可以直接求这个结果

hdu 5212 反向容斥或者莫比

http://acm.hdu.edu.cn/showproblem.php?pid=5212 题意:忽略.. 题解:把题目转化为求每个gcd的贡献.(http://www.cnblogs.com/z1141000271/p/7419717.html 和这题类似 反向容斥)这里先用容斥写了,mobious的之后再说吧23333. 然后比较想说的是这个调和级数的复杂度 nlog(n) ac代码: #include <iostream> #include <cstdio> #includ

hdu 4407 Sum 容斥+离线

求X-Y之间和p互质的数的和,典型的容斥问题,求和用等差数列求和,注意首项末项是多少. 首先记录下不修改的答案,离线处理,存下询问,输出的时候,遇到一个操作1,就遍历前面的操作,把修改加上去,注意要判重,只保留最后一次修改. #include <stdio.h> #include <vector> #include <algorithm> #include <cmath> #include <iostream> #include<cstri

hdu 5514 Frogs 容斥思想+gcd 银牌题

Frogs Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 1509    Accepted Submission(s): 498 Problem Description There are m stones lying on a circle, and n frogs are jumping over them.The stones a