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 imes 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 imes 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 "); 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 "); else if(dp[n]==0)printf("0 "); else if(f[0]%dp[n]==0){ printf("1/%d ",f[0]/dp[n]); }else{ int g=gcd(dp[n],f[0]); printf("%d/%d ",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 "); else if(zi==0){printf("0 ");} 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" ",fz); else printf(LL "/" LL,fz,fm); fclose(stdin); fclose(stdout); return 0; }
T3
题目描述
m imes 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]=min(dlx[d],cy[x]); dld[d]=max(dld[d],cy[x]); } void dfs(int x,int a[][20]) { if(clock()>1950) { printf("%d",ans); fclose(stdin);fclose(stdout); exit(0); } p=judge(a); if(p>=ans) return; if(x==n+1) { if(p<ans) ans=p; return; } for(int i=1;i<=k;i++) { a[i][++a[i][0]]=x; int khx=dhx[i],khd=dhd[i],klx=dlx[i],kld=dld[i]; change(x,i); dfs(x+1,a); a[i][a[i][0]--]=0; dhx[i]=khx;dhd[i]=khd;dlx[i]=klx;dld[i]=kld; } } int main() { freopen("dan.in","r",stdin); freopen("dan.out","w",stdout); scanf("%d%d%d",&m,&k,&n); for(int i=1;i<=k;i++) { dhx[i]=dlx[i]=1001; dhd[i]=dld[i]=-1; } for(int i=1;i<=n;i++) scanf("%d%d",&cx[i],&cy[i]); dfs(1,g); printf("%d",ans); }
AC
#include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; int m,k,n,x[20],y[20],f[17][1<<16],cost[1<<16],s[20],ans; void dfs(int now,int cnt,int res) { if (now==n) { if (res<ans) ans=res; return; } if (res+(k-cnt)*4>=ans) return; for (int a=1;a<=cnt;a++) { int pres=s[a]; s[a]|=(1<<now); dfs(now+1,cnt,res-cost[pres]+cost[s[a]]); s[a]^=(1<<now); } if (cnt<k) { cnt++; s[cnt]=(1<<now); dfs(now+1,cnt,res+4); } } int main() { freopen("dan.in","r",stdin); freopen("dan.out","w",stdout); scanf("%d%d%d",&m,&k,&n); for (int a=0;a<n;a++) scanf("%d%d",&x[a],&y[a]); if (n<=14) { memset(f,0x3f,sizeof(f)); for (int a=1;a<(1<<n);a++) { int sx=1001,mx=-1,sy=1001,my=-1; for (int b=0;b<n;b++) if ((a>>b)&1) { if (x[b]<sx) sx=x[b]; if (x[b]>mx) mx=x[b]; if (y[b]<sy) sy=y[b]; if (y[b]>my) my=y[b]; } f[1][a]=(mx-sx+1)*2+(my-sy+1)*2; } for (int a=2;a<=k;a++) for (int b=0;b<(1<<n);b++) for (int c=b-1;c;c=(c-1)&b) f[a][b]=min(f[a][b],f[a-1][c]+f[1][b^c]); int ans=f[1][(1<<n)-1]; for (int a=2;a<=k;a++) ans=min(ans,f[a][(1<<n)-1]); printf("%d ",ans); } else { ans=4000; for (int a=1;a<(1<<n);a++) { int sx=1001,mx=-1,sy=1001,my=-1; for (int b=0;b<n;b++) if ((a>>b)&1) { if (x[b]<sx) sx=x[b]; if (x[b]>mx) mx=x[b]; if (y[b]<sy) sy=y[b]; if (y[b]>my) my=y[b]; } cost[a]=(mx-sx+1)*2+(my-sy+1)*2; } dfs(0,0,0); printf("%d ",ans); } return 0; }