2010 NEERC Eastern Subregional(Ural 1800~1809)解题小结
2015-02-07 02:10:27
题目很有欧洲赛区的风格... 模拟赛过程不太顺利,习惯于国内的 技巧 and 算法 式解题,而忽略了计算机的最大优势:高速计算,也就是“暴力”!
然后数学题居多...
A - Murphy's Law(Ural 1800):
物理题。注意一下单位,l(cm),h(cm),w(转/分钟)
首先考虑:当重心降到 l/2 的位置时答案就可以确定了(思考),算出时间:t = sqrt(2 * h - l) / g,再算出每转90度需要的时间 : t0 = 90 / (w * 6.0)
这样就可以算出重心降到 l/2 时面包转了k个90度,根据求出的k(mod 4),判断:若k<1 || k > 3,那么是“butter”,否则就是“bread”
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cmath> 5 #include <vector> 6 #include <map> 7 #include <set> 8 #include <stack> 9 #include <queue> 10 #include <string> 11 #include <iostream> 12 #include <algorithm> 13 using namespace std; 14 15 #define MEM(a,b) memset(a,b,sizeof(a)) 16 #define REP(i,n) for(int i=0;i<(n);++i) 17 #define REV(i,n) for(int i=(n-1);i>=0;--i) 18 #define FOR(i,a,b) for(int i=(a);i<=(b);++i) 19 #define RFOR(i,a,b) for(int i=(a);i>=(b);--i) 20 #define getmid(l,r) ((l) + ((r) - (l)) / 2) 21 #define MP(a,b) make_pair(a,b) 22 23 typedef long long ll; 24 typedef pair<int,int> pii; 25 const int INF = (1 << 30) - 1; 26 27 double l,h,w; 28 29 int main(){ 30 scanf("%lf%lf%lf",&l,&h,&w); 31 if(2 * h < l){ 32 printf("butter\n"); 33 return 0; 34 } 35 l /= 100.0; 36 h /= 100.0; 37 w *= 6.0; 38 double t = sqrt((2 * h - l) / 9.81) / (90.0 / w); 39 t = t - (int)(t / 4) * 4; 40 if(t < 1 || t > 3) printf("butter\n"); 41 else printf("bread\n"); 42 return 0; 43 }
E - The Machinegunners in a Playoff(Ural 1804):
题目有点长... 其实就是比两场比赛,首先给出第一场比赛的两队进球数,让你算(1)使得A队可能胜,那么A队在第二场比赛至少进几球。(2)使得B队可能胜,那么A队在第二场比赛至多进几球。嫌判断麻烦... 看到比分最多不超过30.. 暴力枚举然后judge QAQ
1 #include <cstdio> 2 #include <iostream> 3 4 int T; 5 int a,b,c,d,flag; 6 char s[100]; 7 8 bool Judge1(int A,int B,int C,int D){ 9 if(A + C == B + D){ 10 if(flag == 0){ 11 if(C >= B) return true; 12 else return false; 13 } 14 else{ 15 if(A >= D) return true; 16 else return false; 17 } 18 } 19 else if(A + C > B + D) 20 return true; 21 else return false; 22 } 23 24 bool Judge2(int A,int B,int C,int D){ 25 if(A + C == B + D){ 26 if(flag == 0){ 27 if(B >= C) return true; 28 else return false; 29 } 30 else{ 31 if(D >= A) return true; 32 else return false; 33 } 34 } 35 else if(B + D > A + C) 36 return true; 37 else return false; 38 } 39 40 int main(){ 41 scanf("%d",&T); 42 while(T--){ 43 for(int i = 1; i <= 4; ++i) scanf("%s",s); 44 if(s[0] == 'h') flag = 0; 45 else flag = 1; 46 for(int i = 1; i <= 2; ++i) scanf("%s",s); 47 scanf("%d",&a); 48 for(int i = 1; i <= 3; ++i) scanf("%s",s); 49 scanf("%d",&b); 50 scanf("%s",s); 51 for(int i = 0; i <= 30; ++i){ 52 if(Judge1(a,b,i,0)){ 53 c = i; 54 break; 55 } 56 } 57 for(int i = 30; i >= 0; --i){ 58 if(Judge2(a,b,i,30)){ 59 d = i; 60 break; 61 } 62 } 63 printf("%d %d\n",c,d); 64 } 65 return 0; 66 }
F - Chapaev and a Cipher Grille(Ural 1805):
一道以天窗密码为背景的题(orz杰哥)... 比赛时YY了一种类似康托的解法... 细节比较多... 推导也蛮多的- =。
赛后看别人用类似暴力dfs过掉的...
最终还是延续了比赛时的思路,推导了半天(orz...数学君下线了)... 敲了一个 N×N 的算法。
※考虑:n为偶数,选 m = n×n/4 个格子。旋转四次,那么这m个格子都能经旋转获得四个位置,要使每个格子的四个位置都不重复才能覆盖整个图。
我们给n×n图的左上角1/4的区域编号,然后再把这m个点旋转四次。
比如就按样例来,n=4 的时候:
1 2 3 1
3 4 4 2
2 4 4 3
1 3 2 1
因为题目求的是字典序,所以我们不妨把整个图写成一个长为n×n的序列:1 2 3 1 3 4 4 2 2 4 4 3 1 3 2 1 (总:16个数)
※思考:那么我们要在这个序列里面选择1个1,1个2,1个3,1个4... 根据选择的情况我们可以得到一个01序列。
比如: 1 2 3 1 3 4 4 2 2 4 4 3 1 3 2 1 ( 红色为选择的数)
01序列:0 0 0 0 0 0 0 0 1 1 0 1 0 0 0 1 ,其实答案就是要输出这个01序列。
首先可以知道的是,每个数选择一个位置,01序列总共有4*4*4*4=256种,如何求第15个01序列呢?
下面这个过程就有点像康托了,考虑第一个数:1
(1)如果这个数不取,那么由后面15个数组成的01序列总:3*4*4*4=192种。
(2)如果这个数取,那么后面的1都不能取,那么由这16个数可以组成的01序列总:1*4*4*4=64种。(192 + 64 = 256 总数)
显然(1)的字典序比(2)要小,那么我们只要检查K是否 > 192 就可以判断出取不取这个数了。
(i)如果K > 192,那么该位标为1(取),K 减去 192。而且后面与该位相同的数都标记为不能再取,剩余的数能构成的01序列总数:48
(ii)如果K <= 192 ,那么该位标为0(不取),剩余的数能构成的01序列总数:192
以此类推,我们可以判断出所有的位置,这个01串就被构造出来了。
※如果以K = 180为例子:
<1> 判断第一位:K <= 192,那么第一位数不取,标为0 (此后1234能放的位置个数:3,4,4,4)
<2> 判断第二位:若这个数不取,则总数为3*3*4*4=144。
因 K > 144,那么这个数必须取,标为1,且后面所有2都标为不能再取。K 减去 144,为36 (此后1234能放的位置个数:3,0,4,4)
<3> 判断第三位:若这个数不取,则总数为3*3*4=36。
因 K > 12,那么这个数必须取,标为1,且后面所有3都标为不能再取。K 减去 36,为0,结束。
构造出的01串就为:0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0
※用点技巧,vis[]来记录每个数还能放的位置个数,不断更新当前能构成的01串总数... 最后会出现一些小问题
(某些数标记不到,所以如果发现某个数未标记且已经是最后一个了,那么必须取)。O(N×N)
O(N×N):
1 #include <cstdio> 2 3 int N; 4 long long K; 5 int g[110][110],sq[110],vis[110],ans[110]; 6 7 inline void Pre(int n){ 8 int k = n / 2,cnt = 0; 9 for(int i = 1; i <= k; ++i) 10 for(int j = 1; j <= k; ++j) 11 g[i][j] = g[j][n - i + 1] = g[n - i + 1][n - j + 1] 12 = g[n - j + 1][i] = ++cnt; 13 cnt = 0; 14 for(int i = 1; i <= n; ++i) 15 for(int j = 1; j <= n; ++j) 16 sq[++cnt] = g[i][j]; 17 } 18 19 int main(){ 20 scanf("%d%I64d",&N,&K); 21 Pre(N); 22 int k = N * N / 4,len = N * N; 23 long long cur = 1,nxt; 24 for(int i = 1; i <= k; ++i) vis[i] = 4,cur *= 4LL; 25 for(int i = 1; i <= len; ++i) if(vis[sq[i]] > 0){ 26 int now = vis[sq[i]]; 27 nxt = cur / now; 28 if(now - 1) nxt *= (now - 1); 29 if(K > nxt){ 30 K -= nxt; 31 cur -= nxt; 32 ans[i] = 1; 33 vis[sq[i]] = -1; 34 } 35 else{ 36 cur = nxt,vis[sq[i]]--; 37 if(vis[sq[i]] == 0) ans[i] = 1; //该数的最后一个,必须取,不取就没了。 38 } 39 if(!K) break; 40 } 41 for(int i = 1; i <= N; ++i){ 42 for(int j = 1; j <= N; ++j) 43 printf("%d",ans[(i - 1) * N + j]); 44 puts(""); 45 } 46 return 0; 47 }
J - Chapaev and Potatoes:
将四个土豆重排,使得每个土豆有且仅有另一个土豆与其同一行或同一列,求最少重排几个土豆。
思考即可知道,最多重排2个土豆的位置。
发现图为20*20,那么只要枚举重排1个/2个土豆,然后judge即可。
1 #include <cstdio> 2 #include <cmath> 3 #include <cstring> 4 5 int X[5],Y[5]; 6 7 bool Judge(){ 8 int c[5]; 9 memset(c,0,sizeof(c)); 10 int A[5] = {0,X[1],X[2],X[3],X[4]}; 11 int B[5] = {0,Y[1],Y[2],Y[3],Y[4]}; 12 for(int i = 1; i <= 4; ++i){ 13 for(int j = i + 1; j <= 4; ++j){ 14 if(A[i] == A[j] && B[i] == B[j]) 15 return false; 16 if(A[i] == A[j] || B[i] == B[j]) 17 ++c[i],++c[j]; 18 } 19 } 20 for(int i = 1; i <= 4; ++i) if(c[i] != 1) return false; 21 return true; 22 } 23 24 void Print(){ 25 for(int i = 1; i <= 4; ++i) 26 printf("%d %d\n",X[i],Y[i]); 27 } 28 29 int main(){ 30 for(int i = 1; i <= 4; ++i) 31 scanf("%d%d",&X[i],&Y[i]); 32 if(Judge()){ 33 Print(); 34 return 0; 35 } 36 //move 1 37 for(int i = 1; i <= 4; ++i){ 38 int tx = X[i],ty = Y[i]; 39 for(int p = 1; p <= 20; ++p){ 40 for(int q = 1; q <= 20; ++q){ 41 X[i] = p,Y[i] = q; 42 if(Judge()){ 43 Print(); 44 return 0; 45 } 46 } 47 } 48 X[i] = tx,Y[i] = ty; 49 } 50 //move 2 51 for(int i = 1; i <= 4; ++i){ 52 for(int j = 1; j <= 4; ++j){ 53 int tx1 = X[i],ty1 = Y[i]; 54 int tx2 = X[j],ty2 = Y[j]; 55 for(int p1 = 1; p1 <= 20; ++p1){ 56 for(int q1 = 1; q1 <= 20; ++q1){ 57 for(int p2 = 1; p2 <= 20; ++p2){ 58 for(int q2 = 1; q2 <= 20; ++q2){ 59 X[i] = p1,Y[i] = q1; 60 X[j] = p2,Y[j] = q2; 61 if(Judge()){ 62 Print(); 63 return 0; 64 } 65 } 66 } 67 } 68 } 69 X[i] = tx1,Y[i] = ty1; 70 X[j] = tx2,Y[j] = ty2; 71 } 72 } 73 return 0; 74 } 75