APIO2018练习赛伪题解
传送门:https://pcms.university.innopolis.ru/statements/org/apio/2018/practice/statements.pdf
主要就在于后面三道构造题,感觉开阔了眼界。
A:
A + B problem,没看到实数还WA了一发
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=l; i<=r; i++) 5 typedef long long ll; 6 using namespace std; 7 8 int main(){ 9 double a,b; scanf("%lf%lf",&a,&b); printf("%.4lf\n",a+b); 10 return 0; 11 }
B:
翻转数组
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=l; i<=r; i++) 5 typedef long long ll; 6 using namespace std; 7 8 const int N=20010; 9 int n; 10 ll a[N]; 11 12 int main(){ 13 scanf("%d",&n); rep(i,1,n) scanf("%lld",&a[i]); 14 for (int i=n; i; i--) printf("%lld ",a[i]); 15 return 0; 16 }
C:
给定每个数的小数点后有效位数,确定每个小数,使和为1。
所有数都取0.0...1,最后选一个位数最多的作为(1-其余的和),如果为负数则无解。
有几种特殊情况要讨论一下,写一个假的高精度就好。
手调几种情况都没有错,交上去爆零不懂为什么。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=l; i<=r; i++) 5 typedef long long ll; 6 using namespace std; 7 8 const int N=200010; 9 int n,mx,k,l,kk,a[N],d[N]; 10 11 bool add(int x){ 12 for (int i=x; i; i--){ 13 d[i]++; if (d[i]<=9) break; d[i]-=10; 14 } 15 if (d[1]>9) return 1; else return 0; 16 } 17 18 void sub(){ rep(i,1,mx-1) d[i]=9-d[i]; d[mx]=10-d[mx]; } 19 20 int main(){ 21 freopen("c.in","r",stdin); 22 freopen("c.out","w",stdout); 23 scanf("%d",&n); 24 if (n==1) { puts("NO"); return 0; } 25 rep(i,1,n) scanf("%d",&a[i]),mx=max(mx,a[i]); 26 rep(i,1,n) if (a[i]==mx) { k=i; break; } 27 rep(i,1,n) if (a[i]==mx) l++; 28 if (l==1){ puts("NO"); return 0; } 29 if (l%10==1){ rep(i,1,n) if (a[i]==mx && i!=k) { kk=i; break; } add(mx);}; 30 rep(i,1,n) if (i!=k) 31 if (add(a[i])) { puts("NO"); return 0; } 32 sub(); puts("YES"); 33 rep(i,1,n) if (i!=k){ 34 printf("0."); rep(j,2,a[i]) printf("0"); 35 if (i==kk) puts("2"); else puts("1"); 36 }else{ 37 printf("0."); rep(j,1,mx) printf("%d",d[j]); puts(""); 38 } 39 return 0; 40 }
D:
铺地砖,每次可以把两块一起翻90度,构造初始状态到最终状态的方案。
S和T都全部变成横的或竖的,然后把T的方案反过来输出就好了。不存在无解。
1 #include<cstdio> 2 #include<algorithm> 3 #define rep(i,l,r) for (int i=l; i<=r; i++) 4 using namespace std; 5 6 const int N=52,K=100010; 7 char s[N][N]; 8 int n,m,x[K],y[K],xn,cnt,t,k[260]; 9 10 void solve(){ 11 k[t]=0; 12 rep(i,1,n){ 13 scanf("%s",s[i]+1); 14 rep(j,1,m) k[s[i][j]]++; 15 } 16 while (k[t]){ 17 rep(i,1,n) rep(j,1,m) 18 if (s[i][j]=='U' && s[i][j+1]=='U'){ 19 x[++cnt]=i; y[cnt]=j; 20 s[i][j]=s[i+1][j]='L'; s[i][j+1]=s[i+1][j+1]='R'; 21 k['U']-=2; k['L']+=2; 22 } 23 if (!k[t]) return; 24 rep(i,1,n) rep(j,1,m) 25 if (s[i][j]=='L' && s[i+1][j]=='L'){ 26 x[++cnt]=i; y[cnt]=j; 27 s[i][j]=s[i][j+1]='U'; s[i+1][j]=s[i+1][j+1]='D'; 28 k['U']+=2; k['L']-=2; 29 } 30 } 31 } 32 33 int main(){ 34 scanf("%d%d",&n,&m); 35 t=(n&1) ? 'U' : 'L'; 36 solve(); xn=cnt; solve(); printf("%d\n",cnt); 37 rep(i,1,xn) printf("%d %d\n",x[i],y[i]); 38 for (int i=cnt; i>xn; i--) printf("%d %d\n",x[i],y[i]); 39 return 0; 40 }
E:
每次将字符串任意分成两段,后半段翻转放前面,前半段直接放后面,构造S->T的方案,步数尽量少。
据说这种构造转移类题目主要思想是在维护好已构造部分的同时扩大构造范围。
3n做法:https://blog.csdn.net/qq_32506797/article/details/79377650
5/2 n做法 和 2n做法 分别在官网tutorial和tutorial的讨论区里,还没看。
下面是3n做法:
1 #include<cstdio> 2 #include<vector> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=l; i<=r; i++) 5 using namespace std; 6 7 const int N=2010; 8 int n; 9 char su[N], sv[N]; 10 vector<int> vec; 11 12 void shift(int x){ 13 if (x==0) return; 14 reverse(su+1,su+n+1); reverse(su+x+1,su+n+1); 15 vec.push_back(x); 16 } 17 18 int main(){ 19 scanf("%d%*d",&n); 20 scanf("%s", su+1); scanf("%s", sv+1); 21 rep(i,1,n){ 22 int j=i; while (j<=n && su[j]!=sv[n-i+1]) j++; 23 if (j==n+1) { puts("-1"); return 0; } 24 shift(n); shift(j-1); shift(1); 25 } 26 printf("%d\n",(int)vec.size()); 27 for (int i=0; i<vec.size(); i++) printf("%d ",vec[i]); 28 puts(""); 29 return 0; 30 }