题目链接

Problem Description
You are given an array A , and Zhu wants to know there are how many different array B satisfy the following conditions?

1≤Bi≤Ai
For each pair( l , r ) (1≤l≤r≤n) , gcd(bl,bl+1...br)≥2

Input
The first line is an integer T(1≤T≤10) describe the number of test cases.

Each test case begins with an integer number n describe the size of array A.

Then a line contains n numbers describe each element of A

You can assume that 1≤n,Ai≤105

Output
For the kth test case , first output "Case #k: " , then output an integer as answer in a single line . because the answer may be large , so you are only need to output answer mod 109+7

Sample Input
1
4
4 4 4 4

Sample Output
Case #1: 17
题意:
给出长度为n的A数列,求满足条件的B数组的个数,条件:①1<=b[i]<=a[i] ②对于任意区间【L,R】,区间gcd>=2

看网上大神的代码都是用莫比乌斯反演来求解的,不大理解这个,就说一下我自己的一个思路吧。
定义:
dp[i]表示gcd为i的数的个数, 则b中每个元素都为i的倍数
a数组保存每一个输进去的值,cnt[i]表示小于等于i的数的个数

设d为当前的gcd
b[i]<=a[i] 则第i个位置有a[i]/d种选择 直接累乘TLE.
若a[i]/d=k贡献为k,则和它相同贡献有cnt[kd,(k+1)d-1]个,则按段来枚举,算出该段贡献k^cnt.
最后容斥减掉gcd为jx的部分(j>1)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+20;
const ll mod=1e9+7;
ll dp[N],n,a[N],cnt[N];///dp[i]表示gcd为i的数的个数,a数组保存每一个输进去的值,cnt[i]表示小于等于i的数的个数
ll powmod(ll x,ll n)///快速幂求出x^n取模后的结果
{
    ll s=1;
    while(n)
    {
        if(n&1)
            s=(s*x)%mod;
        n>>=1;
        x=(x*x)%mod;
    }
    return s%mod;
}

int main()
{
    int T;
    scanf("%d",&T);
    int cas=0;
    while(T--)
    {
        scanf("%d",&n);
        memset(cnt,0,sizeof(cnt));
        memset(dp,0,sizeof(dp));
        ll mx=0;
        for(int i=1; i<=n; i++)
        {
            scanf("%lld",&a[i]);
            mx=max(mx,a[i]);///mx表示输进去的这些数的最小值
            cnt[a[i]]++;
        }
        for(int i=1; i<=mx; i++)
            cnt[i]+=cnt[i-1];///cnt最终表示的是小于等于i的数的个数
        ll ans=0;
        for(int i=mx; i>=2; i--)///当前是以i为gcd
        {
            ll res=1;
            if(cnt[i-1])///压根就不存在比i小的数,那么也不可能有以i为gcd的数
            {
                dp[i]=0;
                continue;
            }
            for(int j=i; j<=mx; j+=i)///j都是i的倍数
            {
                ll num=cnt[min(mx,(ll)j+i-1)]-cnt[j-1];//[ki~(k+1)i),在这个标准下的输的个数
                ll x=j/i;///贡献为x
                if(num)
                    res=(res*powmod(x,num))%mod;///这里的含义可以理解为每个数都有x种选法,现在一共有num个数,则应该是x^num
            }
            dp[i]=res;
        }
        for(int i=mx; i>=2; i--)
        {
            for(int j=i+i; j<=mx; j+=i)
                dp[i]=(dp[i]-dp[j]+mod)%mod;
            ans=(ans+dp[i])%mod;
        }
        printf("Case #%d: %lld\n",++cas,ans);
    }
    return 0;
}
posted on 2017-07-28 11:04  渡……  阅读(212)  评论(0编辑  收藏  举报