1. 程式人生 > >HDU 6053 TrickGCD 容斥

HDU 6053 TrickGCD 容斥

name long oid i++ typedef etc air continue int

題目鏈接:

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;
}

HDU 6053 TrickGCD 容斥