@CQUPTacm
2018-11-14T06:01:46.000000Z
字数 9003
阅读 3264
题解
按照题意的描述把数列的每个数的平方加起来就行了。善良的出题人保证了答案在int的表示范围之内。
时间复杂度o(T*N),空间复杂度o(N)。
代码
//C++#include<iostream>#include<cstdio>using namespace std;int num[1005];int main(){int t,n,ans;scanf("%d",&t);while(t--){scanf("%d",&n);ans=0;for(int i=1;i<=n;i++){scanf("%d",&num[i]);ans+=num[i]*num[i];}printf("%d\n",ans);}return 0;}
//Javaimport java.util.*;import java.io.*;public class Main {public static void main(String[] args){Scanner cin = new Scanner(System.in);int t=cin.nextInt();int n,ans;int[] num=new int[1005];while(t>0){t--;n=cin.nextInt();ans=0;for(int i=1;i<=n;i++){num[i]=cin.nextInt();ans+=num[i]*num[i];}System.out.println(ans);}}}
根据中位数的定义,如果我们先把数列排序,那么当N为奇数时,中位数就是数列中间位置的那个数,当N为偶数时,中位数就是数列中间位置的那两个数的平均值。
对于N<=1000的数据规模,我们可以采取冒泡排序或者选择排序等时间复杂度为o(N^2)的方法。这里提供的是选择排序的版本。
时间复杂度o(T*N^2),空间复杂度o(N)。
代码
//C++#include<iostream>#include<cstdio>using namespace std;int num[1005];int main(){int t,n,temp;double ans;scanf("%d",&t);while(t--){scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",&num[i]);for(int i=1;i<n;i++)for(int j=i+1;j<=n;j++){if(num[j]<num[i]){temp=num[i];num[i]=num[j];num[j]=temp;}}if(n%2==1)ans=num[(n+1)/2];elseans=(num[n/2]+num[n/2+1])*0.5;printf("%.2f\n",ans);}return 0;}
//Javaimport java.util.*;import java.io.*;public class Main {public static void main(String[] args){Scanner cin = new Scanner(System.in);int t=cin.nextInt();int n,temp;double ans;int[] num=new int[1005];while(t>0){t--;n=cin.nextInt();for(int i=1;i<=n;i++)num[i]=cin.nextInt();for(int i=1;i<n;i++)for(int j=i+1;j<=n;j++){if(num[j]<num[i]){temp=num[i];num[i]=num[j];num[j]=temp;}}if(n%2==1)ans=num[(n+1)/2];elseans=(num[n/2]+num[n/2+1])*0.5;System.out.println(String.format("%.2f", ans));}}}
枚举从(a,b]中的每个数,如果这个数满足题目的两个条件,那么安排数的个数加一。最后输出有多少个安排数即可。
时间复杂度o(T*(b-a)),空间复杂度o(1)。
代码
//C++#include<iostream>#include<cstdio>using namespace std;bool have7(int x){while(x!=0){if(x%10==7)return 1;x=x/10;}return 0;}int main(){int t,a,b,ans;scanf("%d",&t);while(t--){scanf("%d%d",&a,&b);ans=0;for(int i=a+1;i<=b;i++){if(i%7==0 && have7(i))ans++;}printf("%d\n",ans);}return 0;}
//Javaimport java.util.*;import java.io.*;public class Main {public static boolean have7(int x){while(x!=0){if(x%10==7)return true;x=x/10;}return false;}public static void main(String[] args){Scanner cin = new Scanner(System.in);int t=cin.nextInt();int a,b,ans;while(t>0){t--;ans=0;a=cin.nextInt();b=cin.nextInt();for(int i=a+1;i<=b;i++){if(i%7==0 && have7(i))ans++;}System.out.println(ans);}}}
题目和B几乎相同,区别在于数据范围。
对于N<=100000而且ai<=1000000000的数据规模,我们可以采取快速排序、归并排序或者堆排序等时间复杂度为o(N*log(N))的方法。这里提供的是库函数排序的版本。
这里给出一个三种常见o(N*log(N))排序方法的伪代码版本(java),有兴趣的同学可以查看,并自行进行更深入的了解。blog.csdn.net/wkyworkuno/article/details/66091709
时间复杂度o(T*N*log(N)),空间复杂度o(N)。
代码
//C++#include<iostream>#include<cstdio>#include<algorithm>using namespace std;int num[100005];int main(){int t,n,temp;double ans;scanf("%d",&t);while(t--){scanf("%d",&n);ans=0;for(int i=1;i<=n;i++)scanf("%d",&num[i]);sort(num+1,num+1+n);if(n%2==1)ans=num[(n+1)/2];elseans=(num[n/2]+num[n/2+1])*0.5;printf("%.2f\n",ans);}return 0;}
//Javaimport java.util.*;import java.io.*;public class Main {public static void main(String[] args){Scanner cin = new Scanner(System.in);int t=cin.nextInt();int n,temp;double ans;int[] num=new int[100005];while(t>0){t--;n=cin.nextInt();for(int i=1;i<=n;i++)num[i]=cin.nextInt();Arrays.sort(num,1,1+n);if(n%2==1)ans=num[(n+1)/2];elseans=(num[n/2]+num[n/2+1])*0.5;System.out.println(String.format("%.2f", ans));}}}
题目和C几乎相同,区别在于数据范围。
对于T<=100000的数据范围,如果时间复杂度还是o(T*(b-a)),那么肯定会超时。
这里我们要用到一个小技巧:区间和等于两个前缀和的差。
具体来说,如果S[i]表示a[1]+a[2]+……+a[i]的和,那么a[l+1]+a[l+2]+……+a[r]就可以用S[r]-S[l]来表示。所以我们只需要求出S数组即可。
时间复杂度o(max(b)+T),空间复杂度o(max(b)+T)。
代码
//C++#include<iostream>#include<cstdio>using namespace std;int ans[1000005];bool have7(int x){while(x!=0){if(x%10==7)return 1;x=x/10;}return 0;}int main(){int t,a,b;scanf("%d",&t);for(int i=1;i<=1000000;i++){ans[i]=ans[i-1];if(i%7==0 && have7(i))ans[i]++;}while(t--){scanf("%d%d",&a,&b);printf("%d\n",ans[b]-ans[a]);}return 0;}
//Javaimport java.util.*;import java.io.*;public class Main {public static boolean have7(int x){while(x!=0){if(x%10==7)return true;x=x/10;}return false;}public static void main(String[] args){Scanner cin = new Scanner(System.in);int t=cin.nextInt();int[] ans=new int[1000005];int a,b;for(int i=1;i<=1000000;i++){ans[i]=ans[i-1];if(i%7==0 && have7(i))ans[i]++;}while(t>0){t--;a=cin.nextInt();b=cin.nextInt();System.out.println(ans[b]-ans[a]);}}}
朴素的想法是枚举数列中的任何一对数,求出他们的最大公因数加到答案中。这样计算一组数据的时间复杂度o(f*N^2),这里的f指的是求两个数的gcd所耗费的时间。即使这个f非常小,光是N^2已经很大了,而且还要乘上组数T,所以这样肯定会超时。
我们换个思路:如果x和y都是z的倍数,那么gcd(x,y)肯定是z的倍数。如果有k个数都是z的倍数,那么在这k个数中任意选择一对,他们的gcd肯定也是z的倍数,所以我们只需要求出对于每个z数列中有多少个数是z的倍数即可。如果对于每个z,我们知道数列中有多少对数字的gcd恰好等于z,那么我们把对数和z相乘,就是这个值对答案的贡献,枚举z把贡献算出来全加上,就是答案。
我们注意到,之前我们说的是gcd是z的倍数,但是我们要求的是gcd恰好为z的。所以我们要将gcd是z的倍数(包括z)的数字有多少对,减去gcd是z的倍数(不包括z)的数字有多少对,剩下的就是gcd恰好为z的。举个例子:我们用gcd=5的倍数对数减去gcd恰好为10、gcd恰好为15、……剩下的就是gcd恰好为5的。
关于如何计算每个z在数列中有多少个倍数,我们可以反过来,对于数列中每个数,把它的因数的出现次数加一。枚举x的因数的时间复杂度是o(sqrt(x))。
同样,我们也不必枚举每个z的倍数再减去。我们可以从大到小枚举z,当前z的每个因数(除了z本身),都要减去gcd恰好为z的,我们用另外一个数组储存。同样,对于每个z,枚举因数的时间复杂度是o(sqrt(z))。
这样我们可以从大到小算出gcd恰好等于每个z的数字的对数,乘上z再加起来,就是最终答案。因为两个正整数的gcd肯定小于等于他们当中较小的那个,所以z不会超过数列数字的上限。
时间复杂度o(T*(max(a)+N)*sqrt(max(a))),空间复杂度o(max(a)+N)。
代码
//C++#include<iostream>#include<cstdio>using namespace std;int a[10005];long long del[10005];long long nums[10005];long long ans[10005];int main(){int t,n;long long sumans;scanf("%d",&t);while(t--){scanf("%d",&n);sumans=0;for(int i=1;i<=10000;i++)nums[i]=0;for(int i=1;i<=n;i++){scanf("%d",&a[i]);for(int j=1;j*j<=a[i];j++){if(a[i]%j==0){nums[j]++;if(a[i]/j!=j)nums[a[i]/j]++;}}}for(int i=1;i<=10000;i++)del[i]=0;for(int i=10000;i>1;i--){ans[i]=(nums[i]*(nums[i]-1))/2-del[i];del[1]+=ans[i];for(int j=2;j*j<=i;j++){if(i%j==0){del[j]+=ans[i];if(j!=i/j)del[i/j]+=ans[i];}}}ans[1]=(nums[1]*(nums[1]-1))/2-del[1];for(int i=1;i<=10000;i++)sumans+=i*ans[i];printf("%lld\n",sumans);}return 0;}
//Javaimport java.util.*;import java.io.*;public class Main {public static void main(String[] args){Scanner cin = new Scanner(System.in);int t=cin.nextInt();int[] a=new int[10005];long [] nums=new long[10005];long [] ans=new long[10005];long [] del=new long[10005];long sumans;int n;while(t>0){t--;sumans=0;n=cin.nextInt();for(int i=1;i<=10000;i++)nums[i]=0;for(int i=1;i<=n;i++){a[i]=cin.nextInt();for(int j=1;j*j<=a[i];j++){if(a[i]%j==0){nums[j]++;if(a[i]/j!=j)nums[a[i]/j]++;}}}for(int i=1;i<=10000;i++)del[i]=0;for(int i=10000;i>1;i--){ans[i]=(nums[i]*(nums[i]-1))/2-del[i];del[1]+=ans[i];for(int j=2;j*j<=i;j++){if(i%j==0){del[j]+=ans[i];if(j!=i/j)del[i/j]+=ans[i];}}}ans[1]=(nums[1]*(nums[1]-1))/2-del[1];for(int i=1;i<=10000;i++)sumans+=i*ans[i];System.out.println(sumans);}}}
F(i+1)=F(i)+F(i-1)。
我们构造一个2*1的矩阵M[i]={{F(i-1)},{F(i)}},再构造一个2*2的矩阵A={{0,1},{1,1}},根据矩阵乘法如果M[i]左乘上A,结果是一个2*1的矩阵{{F[i]},{F[i]+F[i-1]}}即{{F[i]},{F[i+1]}},我们记为矩阵M[i+1]。很明显,M[i]左乘j次A,结果就是M[i+j]。
S1(i+1)=S1(i)+F(i+1)=S1(i)+F(i)+F(i-1)。
S2(i+1)=S2(i)+S1(i+1)=S2(i)+S1(i)+F(i+1)=S2(i)+S1(i)+F(i)+F(i-1)。
以此类推。
可以想象,如果我们构造一个10*1的矩阵M[i]={{F(i-1)},{F(i)},{S1(i)},{S2(i)},{S3(i)},{S4(i)},{S5(i)},{S6(i)},{S7(i)},{S8(i)}},也会存在一个10*10的矩阵A令A*M[i]=M[i+1]。
这样我们可以通过构造M[1]和A,让M[1]左乘N-1次A求得M[N],M[N]的最后一行就是S8(N)。这样做的时间复杂度是o(100*N),不用我说你们也知道会超时了,这甚至比暴力计算还满。
但是矩阵乘法是满足结合律的(圈起来,这个要考)。我们可以将A的N-1次方进行二进制分解,比如A^7=A^4*A^2*A,如果我们事先求得矩阵A的1次方(即A本身)、2次方、4次方、8次方、16次方……我们就可以通过将这些矩阵中的一部分相乘求出A^(N-1)。很容易看出来N最多分解为log2(N)项。所以时间复杂度就变成了o(1000*log2(N))。这里的1000表示的是10*10的两个方阵相乘的复杂度,而之前的100指的是10*1的矩阵左乘10*10的矩阵。
时间复杂度o(T*1000*log2(N)),空间复杂度o(100)。
代码
//C++#include<iostream>#include<cstdio>using namespace std;#define mod 1000000007struct Node{long long num[10][10];friend Node operator *(Node x1,Node x2){Node x3;for(int i=0;i<10;i++)for(int j=0;j<10;j++)x3.num[i][j]=0;for(int i=0;i<10;i++)for(int k=0;k<10;k++)for(int j=0;j<10;j++)x3.num[i][j]=(x3.num[i][j]+x1.num[i][k]*x2.num[k][j])%mod;return x3;}}A,E;Node ksm(long long y){Node nowans=E,x=A;while(y){if(y%2)nowans=x*nowans;x=x*x;y=y/2;}return nowans;}int main(){for(int i=0;i<10;i++)E.num[i][i]=1;A.num[0][1]=1;for(int i=1;i<10;i++)for(int j=0;j<=i;j++)A.num[i][j]=1;int t;long long n,ans;scanf("%d",&t);while(t--){scanf("%lld",&n);ans=0;Node now=ksm(n-1);for(int i=1;i<10;i++)ans=(ans+now.num[9][i])%mod;printf("%lld\n",ans);}return 0;}
//Javaimport java.util.*;import java.io.*;class Node{long[][] num=new long[10][10];Node mul(Node x2){Node x3=new Node();for(int i=0;i<10;i++)for(int k=0;k<10;k++)for(int j=0;j<10;j++)x3.num[i][j]=(x3.num[i][j]+num[i][k]*x2.num[k][j])%1000000007;return x3;}}public class Main {static Node E=new Node(),A=new Node();static Node ksm(long y){Node nowans=E,x=A;while(y!=0){if(y%2==1)nowans=nowans.mul(x);x=x.mul(x);y=y/2;}return nowans;}public static void main(String[] args){Scanner cin = new Scanner(System.in);for(int i=0;i<10;i++)E.num[i][i]=1;A.num[0][1]=1;for(int i=1;i<10;i++)for(int j=0;j<=i;j++)A.num[i][j]=1;int t;long n,ans;t=cin.nextInt();while(t>0){t--;n=cin.nextLong();ans=0;Node now=ksm(n-1);for(int i=1;i<10;i++)ans=(ans+now.num[9][i])%1000000007;System.out.println(ans);}}}