训练赛20160417 题解
Current Time: | 2016-04-17 21:00:48 | Contest Type: | Public |
Start Time: | 2016-04-17 12:00:00 | Contest Status: | Ended |
End Time: | 2016-04-17 17:00:00 | Manager: | hrbustacm |
ID
|
Origin
|
Title
| ||
---|---|---|---|---|
![]() |
27 / 37 | Problem A | FZU 1909 | An Equation |
0 / 6 | Problem B | FZU 1910 | Boys and Girls | |
![]() |
3 / 6 | Problem C | FZU 1911 | Construct a Matrix |
1 / 2 | Problem D | FZU 1912 | Divisibility by Thirty-six | |
![]() |
24 / 30 | Problem E | FZU 1913 | Easy Comparison |
![]() |
11 / 58 | Problem F | FZU 1914 | Funny Positive Sequence |
Problem G | FZU 1915 | Graph Traversal | ||
0 / 1 | Problem H | FZU 1916 | Harmonious Substring Function | |
Problem I | FZU 1917 | Interpreter | ||
Problem J | FZU 1918 | John’s Direction | ||
![]() |
0 / 22 | Problem K | FZU 1919 | K-way Merging sort |
![]() |
16 / 32 | Problem L | FZU 1920 | Left Mouse Button |
————————————————————————————————————————————————————————————————————————————————————————————
————————————————————————————————————————————————————————————————————————————————————————————
————————————————————————————————————————————————————————————————————————————————————————————
今天比赛好算正常,不过赛后补题很是心酸,尼玛-------》》》》K题,比赛时和队友一起完成了这道题,java大数,样例都过了,不过还是没过,赛后看的题解,发现没什么区别,找了将近一个小时,没找到错误,我和队友放弃,吃饭去了,吃完回来我继续做,最后没办法了,。照着答案敲一遍,还是没对,尼玛,提交20.30次,现在都没过,,,,,,,,,,,脑袋痛
A题:水题,直接排序,判断第一个和最后一个之和和中间两个之和就可以了
#include<stdio.h> #include<string.h> #include<iostream> #include<algorithm> #include<math.h> using namespace std; int n; int a[10]; int main(){ int cas=1; scanf("%d",&n); while(n--){ for(int i=0;i<4;i++) scanf("%d",&a[i]); sort(a,a+4); if((a[0]+a[3])==(a[1]+a[2])) printf("Case %d: Yes\n",cas++); else printf("Case %d: No\n",cas++); } return 0; }
C题:矩阵快速幂,,,,,给出的n其实就是让你求出s(n)的值,而S(N)的值就是你需要构造的矩形的边界长度,而n的范围比较大,所以需要用矩阵快速幂,这个很容易想到,用矩阵快速幂可以很快求出
各个项。之后由于我们要的前n项之和摸m,fibonacci有一个简单的求和公式s(n)=2*f[n-1]+f[n-2]-1,所有我们就直接求出了矩形的长度,下面就是构造矩形,矩形的构造其实很容易发现边的长度
为奇数的时候是构造不了的,因为总h会有一列一行相等,但是偶数的时候,我们可以对称构造,比如当n==4
就可以对称
1 1 1 -1
1 1 -1 -1
1 0 -1 -1
0 -1 -1 -1
直接暴力输出就可以了
直接1A
#include<iostream> #include<cstdlib> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; struct prog { int a[2][2] ; void init() { a[0][0]=a[1][0]=a[0][1]=1; a[1][1]=0; } void init1() { a[1][0]=a[0][1]=0; a[0][0]= a[1][1]=1; } }; prog matrixmul ( prog a ,prog b , int m) { int i , j , k ; prog c ; for ( i = 0 ; i < 2; i ++ ) { for ( j = 0 ; j < 2 ; j ++ ) { c.a[i][j]=0; for ( k =0 ; k < 2; k ++ ) c.a[i][j]+=(a.a[i][k]*b.a[k][j]%m) ; c.a[i][j] %= m ; } } return c ; } prog mul (prog s , int k , int m) { prog ans ; ans.init1(); while ( k >= 1 ) { if ( k & 1 ) ans = matrixmul ( ans , s , m) ; k = k >> 1 ; s = matrixmul ( s , s , m) ; } return ans ; } int ans[205][205]; int main() { int n, m, ca=1, t; scanf("%d", &t); while(t--) { scanf("%d%d", &n, &m); prog s ; s.init ( ) ; s = mul ( s , n-1 , m) ; int an=s.a[1][0], ann=s.a[0][0]; int sn=(2*ann+an-1)%m; printf("Case %d: ", ca++); if(sn==0||sn%2==1) { printf("No\n"); continue; } if(sn%2==0) { for(int i=1;i<=sn;i++) { for(int j=1;j<=(sn-i);j++) ans[i][j]=1; } for(int i=1;i<=sn;i++) { for(int j=sn;j>(sn-i+1);j--) ans[i][j]=-1; } for(int i=1;i<=sn/2;i++) ans[i][sn-i+1]=1; for(int i=sn/2+1;i<=sn;i++) ans[i][sn-i+1]=0; } printf("Yes\n"); for(int i=1;i<=sn;i++) { for(int j=1;j<=sn;j++) { if(j!=1) printf(" "); printf("%d", ans[i][j]); } printf("\n"); } } return 0; }
E题直接排序暴力比较就可以了
#include<stdio.h> #include<string.h> #include<iostream> #include<algorithm> #include<math.h> using namespace std; int t; char str1[300],str2[300]; int main(){ scanf("%d",&t); int cas=1; while(t--){ int n; scanf("%d",&n); getchar(); for(int i=0;i<n;i++){ char c; scanf("%c",&c); str1[i]=c; str2[i]=c; } getchar(); sort(str2,str2+n); int cnt=0; for(int i=0;i<n;i++) if(str1[i]!=str2[i]) cnt++; printf("Case %d: %d\n",cas++,cnt); } }
F题,还算比较坑,我和队友之前训练赛3,4次坑在了%I64D上面,这次又坑在了这上面,不知道是foj的事还是怎么了,别的oj没遇到过这种情况
一开始我们想到的是使用树状数组,但是发现更新容易,查询费时间,,
后来想到了线性的扫一遍,我们将数列重复一遍排成2*n的长度,方便循环的查询,vis数组被标记的时候代表以当前元素为开头的序列是有负数的,不会成立,我们从后往前从第一个负数开始扫,并累加,只要
在扫到j的时候小于0,就说明以j元素开始的循环序列不成立,当大于0的时候,我们在找下一个负数,以此类推,被%I64d坑了好几次,尼玛
最后n-vis数组中被标记的就可以了
#include<stdio.h> #include<string.h> #include<iostream> #include<queue> #include<algorithm> #include<math.h> using namespace std; typedef long long int LL; LL a[2*500005]; int vis[500005]; int main() { int t; scanf("%d",&t); int ca=1; while(t--) { int n; scanf("%d", &n); memset(vis, 0, sizeof(vis)); for(int i=0;i<n;i++) { scanf("%lld", &a[i]); a[i+n]=a[i]; } int ans=0; for(int i=2*n-1;i>=n;i--) { if(i<n) break; if(a[i]<=0) { LL sum=0; int j; for(j=i;j>i-n;j--) { sum+=a[j]; if(sum<=0) { if(j>=n) vis[j-n]=1; else vis[j]=1; } else { break; } } i=j; }if(i<n) break; } for(int i=0;i<n;i++) if(vis[i]==1) ans++; printf("Case %d: %d\n", ca++, n-ans); } }
k题,一个简单的递归,我们将n不断的分成k堆,而在回溯的时候我们希望比较的次数最大,,,,,,,n个物品平均分成k堆,我们假设k堆数都是在进行回溯排序好了之后的元素,那么着k堆n个物品进行的
最大的比较次数就是(n-k)*(k-1)+(k-1)*k/2 ,这个公式我相信只要在纸上画一下一眼就看出来了,那么我们在dfs的时候,将他不断的分成k堆,并在回溯的时候将每一个小堆都按照上面的公式就行计算,累加就得出来了,
-------------------------------------》下面的代码我们没有通过,我们赛后找了很久都没找到bug,以后想通了再回来纠正,不过思路是对的,我们赛后看别人的代码和我们的差不多,只是递归求和的顺序不一样
import java.util.*; import java.math.*; public class qwerq { public static void main(String args[]) { int t; Scanner cin=new Scanner(System.in); t=cin.nextInt(); for(int ca=1;ca<=t;ca++) { BigInteger n=cin.nextBigInteger(), m=cin.nextBigInteger(); System.out.printf("Case %d: ", ca); System.out.println(abcd(n, m)); } } public static BigInteger abcd(BigInteger a, BigInteger b) { BigInteger tttttt=new BigInteger("1"), ccccccc= new BigInteger("2"); BigInteger num, div, ccc=new BigInteger("0"); div=a.divide(b); num=a.remainder(b); if(div.compareTo(ccc)==0) { BigInteger aaa=num.subtract(tttttt), bbb=num.multiply(aaa), ans=bbb.divide(ccccccc); return ans; } BigInteger ans1, ans2; ans1=abcd(div, b); if(num.compareTo(ccc)==0) ans2=new BigInteger("0"); else ans2=abcd(div.add(tttttt), b); ans1=ans1.multiply(b.subtract(num)); ans2=ans2.multiply(num); BigInteger a1=a.subtract(b), a2=a1.multiply(b.subtract(tttttt)), a3=b.multiply(b.subtract(tttttt)); a3=a3.divide(ccccccc); BigInteger ans0=a2.add(a3); return ans0.add(ans1.add(ans2)); } }
L题,就是非常水的bfs了,直接敲了就过了,
我们只是需要讨论两种情况,如果没有0的话,我们只需要暴力数字的数量就可以了,要是有0的话,我们的bfs每一个含0的联通快的个数,之后加上没有被标记的数字的数量就可以了,
#include<stdio.h> #include<string.h> #include<iostream> #include<queue> #include<algorithm> #include<math.h> using namespace std; int t; char str[15][15]; bool vis[15][15]; struct node { int x,y; }; struct node1 { int x1,y1; } que1[105]; int n; int net[8][2]= {1,0,0,1,-1,0,0,-1,1,1,1,-1,-1,1,-1,-1}; void bfs(int xx,int yy) { node u,v; u.x=xx,u.y=yy; queue<node>q; q.push(u); while(!q.empty()) { u=q.front(); q.pop(); //bool flag1=false; for(int i=0; i<8; i++) { int tx=u.x+net[i][0]; int ty=u.y+net[i][1]; if((tx>=1&&tx<=n&&ty>=1&&ty<=n&&!vis[tx][ty])){ if(str[tx][ty]=='0'){ v.x=tx; v.y=ty; q.push(v); } vis[tx][ty]=true; } } } } int main() { int t; scanf("%d",&t); int cas=1; while(t--) { bool flag=false; int sx,sy; sx=-1,sy=-1; int cnt=0; int ans=0; scanf("%d",&n); getchar(); memset(vis,false,sizeof(vis)); for(int i=1; i<=n; i++) { for(int j=1; j<=n; j++) { scanf("%c",&str[i][j]); if(str[i][j]=='0') { que1[cnt].x1=i; que1[cnt].y1=j; cnt++; flag=true; } } getchar(); } if(!flag) { for(int i=1; i<=n; i++) { for(int j=1; j<=n; j++) { if(str[i][j]!='@') { ans++; } } } printf("Case %d: %d\n",cas++,ans); } else { for(int i=0; i<cnt; i++) { int tx=que1[i].x1; int ty=que1[i].y1; if(!vis[tx][ty]) { vis[tx][ty]=true; bfs(tx,ty); ans++; } } for(int i=1; i<=n; i++) { for(int j=1; j<=n; j++) { if(str[i][j]!='@'&&!vis[i][j]) { ans++; } } } printf("Case %d: %d\n",cas++,ans); } } return 0; }
******************************************************************************************************************************
总的来说,今天最后一道java题没做出来真心很不爽,弱爆了---------------------------------------------------------------------------》》》》》》》》》》》》》》》》》
******************************************************************************************************************************