2015-2016 ACM-ICPC, NEERC, Northern Subregional Contest 训练报告
题目链接:http://codeforces.com/gym/100801
训练情况:AC8题,竟然读错了一道题,wa了挺多发。
说明:AC的代码中加了文件读入。
Problem A. Alex Origami Squares
题意:给你一个h*w的矩形,让你分成三个正方形,输出正方形的最大边长。
分析:直接在分成2*2份 3*1份 1*3份,取max就可以了
AC代码:
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5+5; double h, w; int main () { freopen("alex.in", "r", stdin); freopen("alex.out", "w", stdout); cin >> h >> w; double ans = 0; ans = max(ans, min(h*1.0/3, w)); ans = max(ans, min(h, w*1.0/3)); ans = max(ans, min(h*1.0/2, w*1.0/2)); printf("%.3lf\n", ans); return 0; }
Problem B. Black and White
题意:给你b和w,然后让你构造一个n*m的矩阵,是的‘@’符号的联通块个数时b,‘.’的联通块个数是w。
分析:如果b和w一样,我们可以直接输出@和.间隔。否则我们假设b小于w,然后接下来的2*(b-1)行,我们交叉输出@@@和、、、,这样@还差一个联通块,.还差w-b+1个联通块。接下来我们输出2*(w-b+1)行,交叉输出@@@和@.@,就可以保证输出满足要求了。b>w交换一下就可以。
AC代码:
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 int main(){ 6 ios_base::sync_with_stdio(false); 7 cin.tie(0); 8 freopen("black.in","r",stdin); 9 freopen("black.out","w",stdout); 10 int b,w,r,c; 11 while(cin>>b>>w){ 12 13 if(b==w){ 14 cout<<1<<" "<<b+w<<endl; 15 for(int i=1;i<=b+w;i++){ 16 if(i%2==0){ 17 cout<<"@"; 18 } 19 else cout<<"."; 20 } 21 cout<<endl; 22 } 23 else { 24 char s1='@',s2='.'; 25 if(b>w){ 26 swap(b,w); 27 swap(s1,s2); 28 } 29 int d=min(b,w); 30 d--; 31 cout<<d*2+max(b-d,w-d)*2<<" "<<3<<endl; 32 for(int i=1;i<=d*2;i++){ 33 if(i%2==1){ 34 cout<<s1<<s1<<s1<<endl; 35 } 36 else cout<<s2<<s2<<s2<<endl; 37 } 38 d=max(b-d,w-d); 39 for(int i=1;i<=d;i++){ 40 for(int j=1;j<=2;j++){ 41 if(j==1){ 42 cout<<s1<<s1<<s1<<endl; 43 } 44 else cout<<s1<<s2<<s1<<endl; 45 } 46 } 47 } 48 49 } 50 51 return 0; 52 }
Problem C. Concatenation
题意:给你两个字符串s1和s2,然后在s1中选一个非空前缀,在s2中选一个非空后缀,问一共可以拼接多少不同的字符串。
分析:直接计算s2中每个字符的个数(最后一个字符不进行统计),直接用s1和s2字符串长度想乘,计算全部情况,减去重复的即可。枚举s1的每一个字符(不包括第一个)在第二个中出现几次,求和就是重复出现的个数(可以直接相乘,不用加法).
AC代码:
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 string s; 6 long long mp[505]; 7 int main(){ 8 ios_base::sync_with_stdio(false); 9 cin.tie(0); 10 freopen("concatenation.in","r",stdin); 11 freopen("concatenation.out","w",stdout); 12 string s1,s2; 13 while(cin>>s1>>s2){ 14 long long d1=s1.size(); 15 long long d2=s2.size(); 16 memset(mp,0,sizeof(mp)); 17 long long result=0; 18 result=d1*d2; 19 for(int i=0;i<d2-1;i++){ 20 mp[s2[i]]++; 21 } 22 for(int i=1;i<d1;i++){ 23 result-=mp[s1[i]]; 24 } 25 cout<<result<<endl; 26 } 27 28 return 0; 29 }
Problem D. Distribution in Metagonia
题意:输入t,代表测试组数,接下来每次给你n,范围1e18,让你把n拆成不超过100个数字,这些数字的质因子只有2和3,。输出数字个数,然后
分析:比赛的时候是暴力dfs过的,但是看了一下自己的dfs还有别人的AC思路,应该是刚好这个dfs顺序和贪心的顺序一样,只不过及时return了。然后贪心的话,每次找n可以整除的最大的2^w,然后再找最大p使得2^w*3^p<=n。这样可以保证w是递减的,p是递增的,然后每两个数字都不会互相整除。
AC代码:
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 long long mod=1e18; 6 long long pw2[105],pw3[105]; 7 int up[105]; 8 struct st{ 9 int x,y; 10 }a[105]; 11 int number=0; 12 int q=0; 13 void dfs(long long sum){ 14 // cout<<sum<<endl; 15 if(sum==0) {q=1;return;} 16 if(q==1) return; 17 for(int i=60;i>=0;i--){ 18 if(sum%pw2[i]!=0) continue; 19 for(int j=up[i];j>=0;j--){ 20 if(sum>=pw2[i]*pw3[j]){ 21 a[number].x=i; 22 a[number].y=j; 23 number++; 24 sum-=pw2[i]*pw3[j]; 25 dfs(sum); 26 if(q==1)return; 27 sum+=pw2[i]*pw3[j]; 28 number--; 29 } 30 } 31 } 32 } 33 int main(){ 34 ios_base::sync_with_stdio(false); 35 cin.tie(0); 36 pw2[0]=1; 37 pw3[0]=1; 38 for(int i=1;i<=60;i++){ 39 pw2[i]=pw2[i-1]*2; 40 } 41 for(int i=1;i<=38;i++){ 42 pw3[i]=pw3[i-1]*3; 43 } 44 for(int i=0;i<=60;i++){ 45 long long now=1; 46 for(int j=0;j<=40;j++){ 47 if(pw2[i]*pw3[j]>mod){ 48 up[i]=j-1; 49 break; 50 } 51 now*=3; 52 } 53 } 54 freopen("distribution.in","r",stdin); 55 freopen("distribution.out","w",stdout); 56 int t; 57 long long n; 58 cin>>t; 59 while(t--){ 60 number=0; 61 q=0; 62 cin>>n; 63 dfs(n); 64 cout<<number<<endl; 65 for(int i=0;i<number;i++){ 66 if(i!=0) cout<<" "; 67 cout<<pw2[a[i].x]*pw3[a[i].y]; 68 } 69 cout<<endl; 70 } 71 return 0; 72 }
Problem E. Easy Arithmetic
题意:就是给你一个运算式子,然后你可以添加+或者-,使得运算结果最大。
分析:显然如果是加法,我们不需要添加,如果是减法,只需要在第一个数字之后添加+就可以了。添加加的前提是后一个是数字。
AC代码:
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 int main(){ 6 ios_base::sync_with_stdio(false); 7 cin.tie(0); 8 freopen("easy.in","r",stdin); 9 freopen("easy.out","w",stdout); 10 string s; 11 while(cin>>s){ 12 int d=s.size(); 13 int p=0; 14 for(int i=0;i<d;i++){ 15 //cout<<i<<endl; 16 if(s[i]=='-'){ 17 p=1; 18 cout<<s[i]; 19 i++; 20 if(i<d){ 21 cout<<s[i]; 22 } 23 } 24 else if(p==1){ 25 if(s[i]=='0'){ 26 cout<<"+"<<s[i]; 27 } 28 else { 29 if(s[i]=='+'){ 30 cout<<s[i]; 31 p=0; 32 continue; 33 } 34 cout<<"+"; 35 cout<<s[i]; 36 p=0; 37 } 38 } 39 else { 40 cout<<s[i]; 41 } 42 } 43 cout<<endl; 44 } 45 46 return 0; 47 }
Problem H. Hash Code Hacker
题意:给你一个k,然后让你构造k个字符串,使得字符串按照题目给的哈希公式哈希值相同。
分析:显然哈希公式是31进制,因此上一个字符-1,下一个字符+31就可以了。例如BB和Aa是等价的,可以构造一个长度为200的BBBBB字符串,然后选取两处替换成Aa就可以了。
AC代码:
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 string s; 6 int main(){ 7 ios_base::sync_with_stdio(false); 8 cin.tie(0); 9 freopen("hash.in","r",stdin); 10 freopen("hash.out","w",stdout); 11 int n; 12 s=""; 13 for(int i=1;i<=200;i++){ 14 s+="B"; 15 } 16 while(cin>>n){ 17 int ans=0; 18 for(int i=0;i<100;i++){ 19 if(ans==n) break; 20 for(int j=i;j<100;j++){ 21 ans++; 22 if(i==j){ 23 s[i*2]='A'; 24 s[i*2+1]='a'; 25 cout<<s<<endl; 26 s[i*2]='B'; 27 s[i*2+1]='B'; 28 } 29 else { 30 s[i*2]='A'; 31 s[i*2+1]='a'; 32 s[j*2]='A'; 33 s[j*2+1]='a'; 34 cout<<s<<endl; 35 s[i*2]='B'; 36 s[i*2+1]='B'; 37 s[j*2]='B'; 38 s[j*2+1]='B'; 39 } 40 if(ans==n) break; 41 } 42 } 43 } 44 45 return 0; 46 }
Problem J. Journey to the “The World’s Start”
题意:给你n和t,n代表n个站台,t代表花费的时间,然后接下来一行给你n-1个数字,分别代表每次可以走小于等于i站的地铁票的价格,每种票买到以后可以无限使用,再下来一行给你n-2个数字,代表你在第2到第n-1站下车再上车花费的时间。现在问你从1出发,在花费时间小于等于t的情况下,最少花费多少可以到达n。(从1到n车上需要花费n-1时间)
分析:首先对于题目,我们需要知道两点:1.如果x站的票可以在t时间内到达,那么x+1站的票也一定可以到达。2.一定是只买一种票花费最少。那么我们就可以二分x,判断x站的票是否可以达到我们可以dp+线段树维护,时间复杂度nlogn,然后对于每一个满足的x,维护一下价钱花费的后缀最小值,代表比x大的票都可以到达的最小花费。这样就可以完成这道题,总时间复杂度nloglog。(借用了一发赤耳的线段树模板)
AC代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define IO ios_base::sync_with_stdio(0),cin.tie(0) 4 #define mem(a,b) memset(a,b,sizeof(a)) 5 #define FIN freopen("in.txt","r",stdin) 6 #define ls (id<<1) 7 #define rs ((id<<1)|1) 8 #define mid ((l+r)>>1) 9 typedef unsigned long long ULL; 10 typedef long long LL; 11 12 const int maxn = 50000+5; 13 14 int n, price[maxn]; 15 LL t, a[maxn],b[maxn]; 16 17 struct Node { 18 LL minx; 19 } node[maxn*4]; 20 void pushUp (int id, int l, int r) { 21 node[id].minx = min(node[ls].minx, node[rs].minx); 22 return ; 23 } 24 void build (int id, int l, int r) { 25 if (l == r) { 26 node[id].minx = a[l]; 27 return ; 28 } 29 build(ls, l, mid); 30 build(rs, mid+1, r); 31 pushUp(id, l, r); 32 } 33 void update (int id, int l, int r, int p, LL val) { 34 if (l > r) return ; 35 if (l == r && l == p) { 36 node[id].minx = val; 37 return ; 38 } 39 if (p <= mid) update(ls, l, mid, p, val); 40 else if (p > mid) 41 update(rs, mid+1, r, p, val); 42 pushUp(id, l, r); 43 } 44 LL query (int id, int l, int r, int ql, int qr) { 45 if (l == ql && r == qr) return node[id].minx; 46 if (qr <= mid) return query(ls, l, mid, ql, qr); 47 else if (ql > mid) 48 return query(rs, mid+1, r, ql, qr); 49 else { 50 return min(query(ls, l, mid, ql, mid), query(rs, mid+1, r, mid+1, qr)); 51 } 52 } 53 bool judge(int x){ 54 for(int i=3;i<=n;i++){ 55 long long d=query(1,1,n,max(i-x,1),i-1)+query(1,1,n,i,i); 56 update(1,1,n,i,d); 57 } 58 if(query(1,1,n,n,n)<=t) return true; 59 return false; 60 } 61 void up(){ 62 for(int i=1;i<=n;i++){ 63 a[i]=b[i]; 64 } 65 build(1,1,n); 66 } 67 int main () { 68 ios_base::sync_with_stdio(false); 69 cin.tie(0); 70 freopen("journey.in","r",stdin); 71 freopen("journey.out","w",stdout); 72 cin>>n>>t; 73 t -= (n-1); 74 for (int i = 1; i <= n-1; ++i) 75 cin >> price[i]; 76 for(int i=n-2;i>=1;i--){ 77 price[i]=min(price[i+1],price[i]); 78 } 79 b[1]=0; 80 b[n]=0; 81 for (int i = 2; i <= n-1; ++i) 82 {cin >> a[i]; 83 b[i]=a[i]; 84 } 85 a[1] = a[n] =0; 86 build(1, 1, n); 87 int result=100005; 88 int low=1,high=n-1; 89 while(low<=high){ 90 int md=(low+high)/2; 91 if(judge(md)){ 92 high=md-1; 93 result=min(result,price[md]); 94 } 95 else { 96 low=md+1; 97 } 98 up(); 99 } 100 cout<<result<<endl; 101 return 0; 102 }
Problem L. Lucky Chances
题意:就是给你一个r*c的数组,然后对于每一个数字,如果他在上下左右四个方向,有几个方向是最大的,就可以得几分,问你这个数组一共可以得几分。
分析:维护下行列前后缀最大值就可以,然后枚举就行了。(这个人代码真丑,竟然出现在我的博客上)
AC代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define IO ios_base::sync_with_stdio(0),cin.tie(0) 4 #define mem(a,b) memset(a,b,sizeof(a)) 5 #define FIN freopen("in.txt","r",stdin) 6 typedef unsigned long long ULL; 7 typedef long long LL; 8 9 const int maxn = 100+5; 10 11 int n, m, a[maxn][maxn], h1[maxn][maxn], h2[maxn][maxn], l1[maxn][maxn], l2[maxn][maxn]; 12 13 int main () { 14 freopen("lucky.in", "r", stdin); 15 freopen("lucky.out", "w", stdout); 16 //FIN; 17 cin >> n >> m; 18 for (int i = 1; i <= n; ++i) { 19 for (int j = 1; j <= m; ++j) 20 cin >> a[i][j]; 21 } 22 for (int i = 1; i <= n; ++i) { 23 for (int j = 1; j <= m; ++j) { 24 h1[i][j] = max(h1[i][j-1], a[i][j]); 25 } 26 } 27 for (int i = 1; i <= m; ++i) { 28 for (int j = 1; j <= n; ++j) { 29 l1[i][j] = max(l1[i][j-1], a[j][i]); 30 } 31 } 32 for (int i = 1; i <= n; ++i) { 33 for (int j = m; j >= 1; --j) { 34 h2[i][j] = max(h2[i][j+1], a[i][j]); 35 } 36 } 37 for (int i = 1; i <= m; ++i) { 38 for (int j = n; j >= 1; --j) { 39 l2[i][j] = max(l2[i][j+1], a[j][i]); 40 } 41 } 42 int ans = 0; 43 for (int i = 1; i <= n; ++i) { 44 for (int j = 1; j <= m; ++j) { 45 int x = a[i][j]; 46 if (x > h1[i][j-1]) 47 ans++; 48 if (x > h2[i][j+1]) 49 ans++; 50 } 51 } 52 for (int i = 1; i <= m; ++i) { 53 for (int j = 1; j <= n; ++j) { 54 int x = a[j][i]; 55 if (x > l1[i][j-1]) 56 ans++; 57 if (x > l2[i][j+1]) 58 ans++; 59 } 60 } 61 cout << ans << endl; 62 return 0; 63 }