• 2017ACM暑期多校联合训练


    题目链接

    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
    ",++cas,ans);
        }
        return 0;
    }
    
  • 相关阅读:
    工信部计算机系统集成资质(高级)项目经理
    cout cerr clog
    Cstyle 字符串小示例
    泛型<编程>:基于策略的basic_string实现
    string中c_str()、data()、copy(p,n)函数的用法
    C++引用与const引用比较
    深入了解scanf/getchar/gets/cin等函数(转载)
    使用ifstream和getline读取文件内容[c++]
    const参数,const返回值与const函数 .
    C++ limits头文件的用法(numeric_limits)
  • 原文地址:https://www.cnblogs.com/cmmdc/p/7249131.html
Copyright © 2020-2023  润新知