9.4模拟赛解题报告

\(T1~~~~计数~~~~count\)

题目描述

给出\(m\)个数\(a[1],a[2],…,a[m]\)

求1~n中有多少数不是\(a[1],a[2],…,a[m]\)的倍数。

对于\(60\%\)的数据\(1<=n<=10^6\)对于另外\(20%\)的数据,\(m=2\)

对于\(100\%\)的数据,\(1<=n<=10^9,0<m<=20,1<=a[i]<=10^9\)

题解

这道题显然是容斥

我们可以统计1~n中有多少数是某个\(a_i\)的倍数

显然1~n中\(x\)的倍数有\(n/x\)

容斥:加上每个\(a_i\)的倍数个数,减去每个同时是\(a_i\)\(a_j\)的倍数的数的个数,加上每个同时是\(a_i,a_j,a_k\)的倍数的数的个数……

\[\sum_{1\leq b_1< b_2 ...< b_k\leq n}(-1)^{k-1}\frac{n}{lcm(a_{b_1},a_{b_2}...a_{b_k})}\]

\(2^m\)枚举二进制表示是哪些数的倍数,求lcm即可

一开始求lcm直接暴力,跑得有些慢,求lcm的复杂度较大

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int n,m,a[25],maxx;
long long ans;
int gcd(int x,int y){
    if(!y) return x;
    return gcd(y,x%y);
}
int lcm(int x,int y){
    if(x<y) swap(x,y);
    return x*y/gcd(x,y);
}
long long solve(int x){
    int t=1,f=-1;
    for(int i=0;i<m;i++)
        if((x>>i)&1){
            t=lcm(t,a[i+1]);
            f=-f;
        }
    return n/t*f;
}
int main()
{
//  freopen("count.in","r",stdin);
//  freopen("count.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
        scanf("%d",&a[i]);
    maxx=(1<<m)-1;
    for(int i=1;i<=maxx;i++)
        ans+=solve(i);
    printf("%d\n",n-ans);
//  fclose(stdin); fclose(stdout);
    return 0;
}

我们发现,求\(lcm(a_{b_1},a_{b_2},...,a_{b_k})\)

我们一定求过\(lcm(a_{b_1},a_{b_2},...,a_{b_{k-1}})\)

我们可以记录下每次求得的lcm值,求k个数的lcm可以用k个数里面的k-1个数的lcm直接求出来

复杂度\(O(2^m(m+\log a_i))\)

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define LL long long
LL n,m,a[25],maxx,ans;
bool mark[1000010];
LL lcm[1048580],f[1048580];
LL gcd(LL x,LL y){
    if(!y) return x;
    return gcd(y,x%y);
}
LL Lcm(LL x,LL y){
    if(x<y) swap(x,y);
    return x*y/gcd(x,y);
}
LL solve(LL x){
    for(LL i=0;i<m;i++)
        if((x>>i)&1){
            lcm[x]=Lcm(a[i+1],lcm[x-(1<<i)]);
            f[x]=-f[x-(1<<i)];
            break;
        }
    return f[x]*n/lcm[x];
}
int main()
{
//  freopen("count.in","r",stdin);
//  freopen("count.out","w",stdout);
    scanf("%lld%lld",&n,&m);
    if(n<=1000000){
        int x;
        for(int i=1;i<=m;i++){
            scanf("%d",&x);
            for(int j=x;j<=n;j+=x)
                mark[j]=1;
        }
        for(int i=1;i<=n;i++)
            if(!mark[i]) ans++;
        printf("%lld\n",ans);
        fclose(stdin); fclose(stdout);
        return 0;
    }
    for(int i=1;i<=m;i++)
        scanf("%lld",&a[i]);
    maxx=(1<<m)-1;
    lcm[0]=1; f[0]=-1;
    for(LL i=1;i<=maxx;i++)
        ans+=solve(i);
    printf("%lld\n",n-ans);
//  fclose(stdin); fclose(stdout);
    return 0;
}

\(T2~~~~区间第k大~~~~kth\)

题目描述

一个区间的价值定义为该区间中的最大值减最小值

给定\(n\)个数,求所有区间价值中,第\(k\)大值为多少。

猜你喜欢

转载自www.cnblogs.com/yjkhhh/p/9593134.html