9.14模拟赛

Posted 小时のblog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了9.14模拟赛相关的知识,希望对你有一定的参考价值。

T1  COGS2524评测

题目描述

1-N1N中找一些数乘起来使得答案是一个完全平方数,求这个完全平方数最大可能是多少。

输入输出格式

输入格式:

 

第一行一个数字NN。

 

输出格式:

 

一行一个整数代表答案对100000007100000007取模之后的答案。

 

输入输出样例

输入样例#1:
7
输出样例#1:
144

说明

对于20\\%20%的数据,1 \\leq N \\leq 1001N100。

对于50\\%50%的数据,1 \\leq N \\leq 50001N5000。

对于70\\%70%的数据,1 \\leq N \\leq 10^51N105​​。

对于100\\%100%的数据,1 \\leq N \\leq 5 \\times 10^61N5×106​​。

 

题解:线性筛+快速幂

如果将完全平方数分解质因数,那么质因数的次数一定都是偶数次的。

将1--n中的质数筛出来,然后找每个质数在1--n中出现了多少次,如果是

偶数次直接贡献答案,奇数次-1贡献答案,因为奇数次要-1,所以丢弃这个

质数本身,不会出现一个数只取了一部分。怎样求1--n中因数x出现的次数呢,

举个例子,1--81中3出现的次数是 81/3+27/3+9/3+3/3=40。

先了解是这么个方法。

假如求1--30中因数3出现的次数,

3          6         9        12        15         18       21      24        27      30

3*1     3*2      3*3      3*4      3*5      3*3*2    3*7    3*8      3*3*3   3*10

表是上下对应的。

数一数3出现了14次。

1--30中能整除一个3的个数有 30/3=10 ,是上面全部的数

1--30中能整除两个3的个数有 30/(3*3) =3 ,是9 18 27

1--30中能整除三个3的个数有  30/(3*3*3) =1,是27,

加起来为 30/3+30/(3*3)+30/(3*3*3)=14,

变一下也就是 30/3+9/3+3/3=14。

上面讲完 ,对于本题讲个例子,求1--6能组成的最大的完全平方数。

1      2      3      4        5       6       

1    1*2   1*3    2*2    1*5   2*3   

其中2出现 5次 3出现2次 5出现一次,奇数次-1,只剩下2^4和3^2,所以答案为2^4*3^2

又理解了一遍 if(i%prime[j]==0)break;如果i是prime[j]的倍数,也就是i=k*prime[j],那么i*prime[j+1]

这个数是下一个被筛去的合数,i*prime[j]=k*prime[j]*prime[j+1]=gg*prime[j]会被prime[j]筛去

保证了O(n)的复杂度。

注意:没开Long long 一直卡 =n=

代码:

 

#include<iostream>
#include<cstdio>
#define mod 100000007
using namespace std;

int n,js;
long long ans=1;
int prime[5000005],check[5000005];

void Prime(){
    check[1]=1;
    for(int i=2;i<=n;i++){
        if(!check[i])prime[++js]=i;
        for(int j=1;j<=js&&i*prime[j]<=n;j++){
            check[i*prime[j]]=1;
            if(i%prime[j]==0)break;
        }
    }
}

int count(int x){
    int tmp=n,res=0;
    while(tmp){
        tmp/=x;
        res+=tmp;
    }
    return res;
}

long long ksm(int x,int y){
    long long now=x,res=1;
    while(y){
        if(y&1)res=1LL*res*now%mod;
        now=now*now%mod;
        y>>=1;
    }
    return res%mod;
}
int main(){
    scanf("%d",&n);
    Prime();
    for(int i=1;i<=n;i++){
        if(!check[i]){
            int k=count(i);
            ans=ans*ksm(i,k/2*2)%mod;
            
        }
    }
    cout<<ans;
    return 0;
}

 

T2

题目描述

NN个数,随机选择一段区间,如果这段区间的所有数的平均值在[l,r][l,r]中则你比较厉害。求你比较厉害的概率。

输入输出格式

输入格式:

 

第一行有三个数N,l,rN,l,r,含义如上描述。

接下来一行有NN个数代表每一个数的值。

 

输出格式:

 

输出一行一个分数\\Large \\frac{a}{b}ba​​代表答案,其中a,ba,b互质。如果答案为整数则直接输出该整数即可。

 

输入输出样例

输入样例#1:
4 2 3
3 1 2 4
输出样例#1:
7/10
输入样例#2:
4 1 4
3 1 2 4
输出样例#2:
1

说明

对于30\\%30%的数据,1 \\leq n \\leq 10^41n104​​。

对于60\\%60%的数据,1 \\leq n \\leq 10^51n105​​。

对于100\\%100%的数据,1 \\leq n \\leq 5 \\times 10^5,0 < l \\leq r \\leq 1001n5×105​​,0<lr100。

 

题目大意:求一段区间内有多少个子区间的平均值在[l,r]范围内。

题解:

暴力10分,前缀和+枚举,也可以递推 注意分母为0 还好随便输入几个数测发现re

 

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

int n,l,r,ans;
int f[2],sum[500008],dp[500005];

inline int read(){
    int x=0,f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch==\'-\')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-\'0\';
    return x*f;
}

int gcd(int x,int y){
    return y==0?x:gcd(y,x%y);
}

int main(){
    freopen("jian.in","r",stdin);
    freopen("jian.out","w",stdout);
    n=read();l=read();r=read();
    for(int i=1;i<=n;i++){
        int x;x=read();
        f[1]=f[0]+i;
        f[0]=f[1];
        sum[i]=sum[i-1]+x;
    }
/*    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(j+i-1>n)break;
            double k=double((sum[j+i-1]-sum[j-1])/i);
            if(k>=l&&k<=r)ans++;
        }
    }
    if(ans==f[0])printf("1\\n");
    else if(f[0]%ans==0)printf("1/%d",f[0]/ans);
    else {
        int g=gcd(ans,f[0]);
        printf("%d/%d",ans/g,f[0]/g);
    }
    */
    for(int i=1;i<=n;i++){
       dp[i]=dp[i-1];
        for(int j=1;j<=i;j++){
            double k=(sum[i]-sum[j-1])/(i-j+1);
            dp[i]+=(k>=l&&k<=r);
        }
    }
    if(f[0]==dp[n])printf("1\\n");
    else
    if(dp[n]==0)printf("0\\n");
    else
    if(f[0]%dp[n]==0){
        printf("1/%d\\n",f[0]/dp[n]);
    }else{
        int g=gcd(dp[n],f[0]);
        printf("%d/%d\\n",dp[n]/g,f[0]/g);
    }
    fclose(stdin);
    fclose(stdout);
    return 0;
}

正解:逆序对....

n个数的和为An,如果区间[1,n]的平均数满足在区间[l,r]的范围内,那么n*l≤An≤n*r,

那么将这n 个数都减去l,并求减去的数的前缀和,那么对于区间[a,b]满足平均值属于[l,r]

的条件是sa-sb>=0 ,那么只要sa-sb<0,即sa<sb,就不满足条件。这就是逆序对....

同理当n个数都减去r,并求前缀和,如果sa-sb>0,不满足条件,即sa>sb,将区间翻转

还是求逆序对个数。补集思想,全部的情况n*(n+1)-不满足的个数为满足的个数。

reverse 函数翻转整个区间 线性的时间复杂度 涨姿势

太神辣 orz

代码

 80

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;

long long n,l,r;
long long res,ans;
long long a[5000005],b[5000005];
long long sum[5000005];

long long gcd(long long x,long long y){
    return y==0?x:gcd(y,x%y);
}

void merge(int lx,int lr){
    if(lx==lr)return;
    long long  mid=(lx+lr)>>1;
    merge(lx,mid);merge(mid+1,lr);
    int ll=lx,k=lx,rr=mid+1;
    while(ll<=mid&&rr<=lr){
        if(sum[ll]<=sum[rr]){
            b[k++]=sum[ll++];
        }else{
            ans+=mid-ll+1;
            b[k++]=sum[rr++];
        }
    }
    while(ll<=mid)b[k++]=sum[ll++];
    while(rr<=lr)b[k++]=sum[rr++];
    for(int i=lx;i<=lr;i++)sum[i]=b[i];
}

int main(){
    scanf("%lld%lld%lld",&n,&l,&r);
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    for(int i=1;i<=n;i++){sum[i]=sum[i-1]+a[i]-l;}
    ans=0;merge(0,n);res+=ans;ans=0;
    for(int i=1;i<=n;i++){sum[i]=sum[i-1]+a[i]-r;}
    reverse(sum+1,sum+n+1);merge(1,n+1);res+=ans;
    long long mu=1LL*n*(n+1)/2,zi=mu-res;
    if(mu==zi)printf("1\\n");
    else if(zi==0){printf("0\\n");}
    else {long long k=gcd(mu,zi);cout<<zi/k<<"/"<<mu/k<<endl;}
    return 0;
}

AC

#include<cstdio>
#include<cstring>
#include<algorithm>
#define name "jian"
#define ll long long
#ifdef unix
#define LL "%lld"
#else
#define LL "%I64d"
#endif
using namespace std;
const int N=1e6+10;
int n,L,R,a[N],b[N];
ll fz,fm,gy,ans,s[N],c[N];
inline const int read(){
    register int x=0,f=1;
    register char ch=getchar();
    while(ch<\'0\'||ch>\'9\'){if(ch==\'-\')f=-1;ch=getchar();}
    while(ch>=\'0\'&&ch<=\'9\'){x=(x<<3)+(x<<1)+ch-\'0\';ch=getchar();}
    return x*f;
}
void binary_chop(int l,int r){
    if(l==r) return ;
    int mid=l+r>>1;
    binary_chop(l,mid);binary_chop(mid+1,r);
    int p=l,q=l,j=mid+1;
    while(p<=mid&&j<=r){
        if(s[p]>s[j]){
            ans+=mid-p+1;
            c[q++]=s[j++];
        }
        else{
            c[q++]=s[p++];
        }
    }
    while(p<=mid) c[q++]=s[p++];
    while(j<=r) c[q++]=s[j++];
    for(int i=l;i<=r;i++) s[i]=c[i];
}
int main(){
    freopen(name".in","r",stdin);
    freopen(name".out","w",stdout);
    n=read();L=read();R=read();
    for(int i=1;i<=n;i++) a[i]=read();
    for(int i=1;i<=n;i++) b[i]=a[i]-L;
    for(int i=1;i<=n+1;i++) s[i]=s[i-1]+b[i-1];
    binary_chop(1,n+1);fz+=ans;
    memset(s,0,sizeof s);ans=0;
    for(int i=1;i<=n;i++) b[i]=a[i]-R;
    for(int i=1;i<=n+1;i++) s[i]=s[i-1]+b[i-1];
    reverse(s+1,s+n+2);
    binary_chop(1,n+1);fz+=ans;
    fm=(ll)n*(n+1)/2;
    fz=fm-fz;
    gy=__gcd(fz,fm);
    fz/=gy;fm/=gy;
    if(fm==1) printf(LL"\\n",fz);
    else printf(LL "/" LL,fz,fm);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

 

 

T3

 

题目描述

m \\times mm×m的方阵上有nn棵葱,你要修一些栅栏把它们围起来。一个栅栏是一段沿着网格建造的封闭图形(即要围成一圈)。各个栅栏之间应该不相交、不重叠且互相不包含。如果你最多修kk个栅栏,那么所有栅栏的长度之和最小是多少?

输入输出格式

输入格式:

 

第一行三个整数m,k,nm,k,n。

接下来nn行每行两个整数x,yx,y代表某棵葱的位置。

 

输出格式:

 

一行一个整数代表答案。

 

输入输出样例

输入样例#1:
6 1 4
1 3
4 2
4 4
6 4
输出样例#1:
18
输入样例#2:
6 2 4
1 3
4 2
4 4
6 4
输出样例#2:
16

说明

对于10\\%10%的数据,k=1k=1。

对于30\\%30%的数据,k \\leq 2k2。

对于60\\%60%的数据,n \\leq 10n10。

对于100\\%100%的数据,1 \\leq k \\leq n \\leq 16,m \\leq 10001kn16,m1000。

 

 

 

题目大意:矩形上有几根葱,用不重叠 不相交 不包含的栅栏围住 求栅栏的最小周长。

栅栏必须是封闭的。

题解:搜索+剪枝

代码:

95卡时

 

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<ctime>
#define maxn 1001
#define minn -1
using namespace std;
int m,k,n,ans=0x7fffffff,p;
int g[20][20];
int cx[20],cy[20];
int dhx[20],dhd[20],dlx[20],dld[20];
int judge(int h[][20])
{
      int s=0;
      for(int i=1;i<=k;i++)
             if(dhd[i]!=-1)
                s+=(dhd[i]-dhx[i]+dld[i]-dlx[i]+2)*2;    
      return s; 
}
void change(int x,int d)
{
    dhx[d]=min(dhx[d],cx[x]);
    dhd[d]=max(dhd[d],cx[x]);
    dlx[d]

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

9.14模拟赛

9.14noip模拟试题

NOIP2016提高A组模拟9.14数列编辑器

《DSP using MATLAB》示例 Example 9.14

9.14考试总结

9.14