9.4模拟赛解题报告

Posted yjkhhh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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_{1leq b_1< b_2 ...< b_kleq 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-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
",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-ans);
//  fclose(stdin); fclose(stdout);
    return 0;
}

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

题目描述

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

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

以上是关于9.4模拟赛解题报告的主要内容,如果未能解决你的问题,请参考以下文章

解题报告 『占卜DIY(模拟)』

NOIP模拟2017.6.11解题报告

一个蒟蒻的挣扎模拟赛2解题报告

2019模拟赛09场解题报告

20161109模拟赛解题报告

POJ 2260(模拟&贪心_B题)解题报告