9.14模拟赛
Posted 小时のblog
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了9.14模拟赛相关的知识,希望对你有一定的参考价值。
T1 COGS2524评测
题目描述
从1-N1−N中找一些数乘起来使得答案是一个完全平方数,求这个完全平方数最大可能是多少。
输入输出格式
输入格式:
第一行一个数字NN。
输出格式:
一行一个整数代表答案对100000007100000007取模之后的答案。
输入输出样例
7
144
说明
对于20\\%20%的数据,1 \\leq N \\leq 1001≤N≤100。
对于50\\%50%的数据,1 \\leq N \\leq 50001≤N≤5000。
对于70\\%70%的数据,1 \\leq N \\leq 10^51≤N≤105。
对于100\\%100%的数据,1 \\leq N \\leq 5 \\times 10^61≤N≤5×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互质。如果答案为整数则直接输出该整数即可。
输入输出样例
4 2 3
3 1 2 4
7/10
4 1 4
3 1 2 4
1
说明
对于30\\%30%的数据,1 \\leq n \\leq 10^41≤n≤104。
对于60\\%60%的数据,1 \\leq n \\leq 10^51≤n≤105。
对于100\\%100%的数据,1 \\leq n \\leq 5 \\times 10^5,0 < l \\leq r \\leq 1001≤n≤5×105,0<l≤r≤100。
题目大意:求一段区间内有多少个子区间的平均值在[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代表某棵葱的位置。
输出格式:
一行一个整数代表答案。
输入输出样例
6 1 4
1 3
4 2
4 4
6 4
18
6 2 4
1 3
4 2
4 4
6 4
16
说明
对于10\\%10%的数据,k=1k=1。
对于30\\%30%的数据,k \\leq 2k≤2。
对于60\\%60%的数据,n \\leq 10n≤10。
对于100\\%100%的数据,1 \\leq k \\leq n \\leq 16,m \\leq 10001≤k≤n≤16,m≤1000。
题目大意:矩形上有几根葱,用不重叠 不相交 不包含的栅栏围住 求栅栏的最小周长。
栅栏必须是封闭的。
题解:搜索+剪枝
代码:
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模拟赛的主要内容,如果未能解决你的问题,请参考以下文章