PKU演習問メモ(8/21)
なんかPKU落ちてて解説記事が書きにくい。後ほど気が向いたら更新。
↑更新。
No. | 問題名 | 問題の種類および解法 | 難易度 |
---|---|---|---|
1102 | LC-Display | 実装 | ★★☆☆☆ |
2413 | How many Fibs? | 多倍長整数 | ★★☆☆☆ |
2305 | Basic remains | 多倍長整数 | ★★☆☆☆ |
2109 | Power of Cryptography | 多倍長整数・二分法 | ★★☆☆☆ |
1454 | Factorial Frequencies | 多倍長整数 | ★★☆☆☆ |
1220 | NUMBER BASE CONVERSION | 多倍長整数・基数変換 | ★★☆☆☆ |
1299 | Polar Explorer | 実装 | ★☆☆☆☆ |
1703 | Find them, Catch them | Union-Find(の応用) | ★★★★☆ |
2272 | Bullseye | 点の距離 | ★☆☆☆☆ |
2812 | Extrusion | 多角形の面積 | ★★☆☆☆ |
1102 LC-Display
問題概要
与えられた数字をLCディスプレイのように出力せよ。
sはサイズ(数字の一辺の長さ)を表す。
s≦10,n≦999,999,999を満たす。
解法
詳細はサンプルを参照せよ、らしい。酷いw
サイズに応じてansに表示を作って、最後に表示。
ソースコード
bool tate[10][3]={ {1,0,1},{0,0,0},{1,1,1},{1,1,1},{0,1,0}, {1,1,1},{1,1,1},{1,0,0},{1,1,1},{1,1,1} }; bool yoko1[10][2]={ {1,1},{0,1},{0,1},{0,1},{1,1}, {1,0},{1,0},{0,1},{1,1},{1,1} }; bool yoko2[10][2]={ {1,1},{0,1},{1,0},{0,1},{0,1}, {0,1},{1,1},{0,1},{1,1},{0,1} }; int s; string str; char ans[1000][1000]; void write(int pos,int d) { rep(i,3)if(tate[d][i])rep(j,s)ans[i*(s+1)][j+pos+1]='-'; rep(i,2) { if(yoko1[d][i])rep(j,s)ans[j+1][pos+i*(s+1)]='|'; if(yoko2[d][i])rep(j,s)ans[s+2+j][pos+i*(s+1)]='|'; } } int main() { while(cin>>s>>str,s) { int n=str.size(); int w=(s+2)*n+n-1,h=2*s+3; //cerr<<w<<" "<<h<<endl; rep(i,h) { fill_n(ans[i],w,' '); ans[i][w]=0; } rep(i,n)write(i*(s+3),str[i]-'0'); rep(i,h)cout<<ans[i]<<endl; cout<<endl; } return 0; }
2413 How many Fibs?
問題概要
二つの整数a,bが与えられる。
a以上b以下のフィボナッチ数はいくつあるか求めよ。
a≦b≦10^100を満たす。
解法
10^100までのフィボナッチ数を全列挙しても500個に満たないので、全列挙してよい。
探索も線形探索で十分間に合う。
ソースコード
class pku2413{ BigInteger a,b; BigInteger[] fib; void run(){ Scanner sc=new Scanner(System.in); fib=new BigInteger[500]; fib[0]=BigInteger.ONE; fib[1]=BigInteger.valueOf(2); int i=2; for(;;i++){ fib[i]=fib[i-1].add(fib[i-2]); if(fib[i].compareTo(BigInteger.TEN.pow(100))>=0)break; } while(true){ a=sc.nextBigInteger(); b=sc.nextBigInteger(); if(a.compareTo(b)==0&&a.compareTo(BigInteger.ZERO)==0)break; int l=0,r=0; for(;fib[l].compareTo(a)<0;l++); for(r=l;fib[r].compareTo(b)<=0;r++); System.out.println(r-l); } } }
2305 Basic remains
問題概要
整数b,p,mが与えられる。p,mはb進数である。
p=a*m+kとなるような負でない最小の整数kを求め、b進数で出力せよ。
bは2から10の整数、pの桁数は1000以下、
0≦m<bを満たす。
解法
BigIntegerを使ってmodを取ればいいが、m=0の場合は場合分けして処理しなければならないことに注意。
ソースコード
class pku2305{ int b; BigInteger p,m; void run(){ Scanner sc=new Scanner(System.in); while(true){ b=sc.nextInt(); if(b==0)break; p=sc.nextBigInteger(b); m=sc.nextBigInteger(b); if(m.compareTo(BigInteger.ZERO)==0)m=BigInteger.ONE; p=p.mod(m); System.out.println(p.toString(b)); } } }
2109 Power of Cryptography
問題概要
与えられた整数n,pに対してk^n=pとなるような整数kを求めよ。
入力に対し、1≦k≦10^9以下で条件を満たす整数kが存在することは保証されている。
n≦200,1≦p≦10^101を満たす。
解法
nは正、kも1以上の整数なのでk^nはkに関する単調増加関数。
したがってkの値で二分法すればよい。
ただしpが大きいのでBigInteger等を使用する。
ソースコード
class pku2199{ int n; BigInteger p; void run(){ Scanner sc=new Scanner(System.in); while(sc.hasNextInt()) { n=sc.nextInt(); p=sc.nextBigInteger(); int lo=1,hi=1000000005,mid=0; while(lo+1<hi){ mid=(lo+hi)/2; if(BigInteger.valueOf(mid).pow(n).compareTo(p)>0)hi=mid; else lo=mid; } System.out.println(lo); } } }
1454 Factorial Frequencies
問題概要
与えられた数nに対し、n!を10進数で表したとき、0-9の数字が出現する回数を求めよ。
n≦366を満たす。
解法
多倍長だと難易度が高くなるのはBigIntegerが書くだけで面倒だから。
Pascalを使えばいいということか。
ソースコード
class pku1454{ int n; void run(){ Scanner sc=new Scanner(System.in); while(true){ n=sc.nextInt(); if(n==0)break; BigInteger fc=BigInteger.ONE; for(int i=1;i<=n;i++)fc=fc.multiply(BigInteger.valueOf(i)); int[] cnt=new int[10]; for(;fc.compareTo(BigInteger.ZERO)>0;fc=fc.divide(BigInteger.TEN)){ int a=fc.mod(BigInteger.TEN).intValue(); cnt[a]++; } System.out.println(n+"! --"); for(int i=0;i<10;i++){ if(i%5==0)System.out.print(" "); System.out.printf("(%d)% 5d", i,cnt[i]); if(i%5==4)System.out.println(); else System.out.print(" "); } } } }
1220 NUMBER BASE CONVERSION
問題概要
a進法で表された数字bが与えられる。これをc進法で表せ。
2≦a≦62,bの各桁はa未満、2≦cを満たす。
数字は9の次はA,B,C...Z,a,b,c...を用いよ。
解法
久しぶりのJava.
基数変換の処理はBigIntegerを使わない普通の場合と変わらない。
結果が0の場合は特別に処理する。
BigIntegerクラスには基数変換のメソッドがついているが、26進法にまでしか対応していないので自力でやる必要がある。
ソースコード
class pku1220{ void run(){ Scanner sc=new Scanner(System.in); int n=sc.nextInt(); for(int i=0;i<n;i++){ int a=sc.nextInt(),b=sc.nextInt(); String c=sc.next(); BigInteger d=BigInteger.ZERO; for(int j=0;j<c.length();j++){ d=d.multiply(BigInteger.valueOf(a)).add(BigInteger.valueOf(val(c.charAt(j)))); } StringBuilder ans=new StringBuilder(); for(;d.compareTo(BigInteger.ZERO)>0;d=d.divide(BigInteger.valueOf(b))) { BigInteger mod=d.mod(BigInteger.valueOf(b)); ans.append(ch(mod.intValue())); } if(ans.length()==0)ans.append('0'); ans.reverse(); System.out.println(a+" "+c); System.out.println(b+" "+ans); System.out.println(); } } int val(char c){ if('0'<=c&&c<='9')return c-'0'; if('A'<=c&&c<='Z')return c-'A'+10; return c-'a'+36; } char ch(int a){ if(a<10)return (char) ('0'+a); if(a<36)return (char) ('A'+a-10); return (char) ('a'+a-36); } }
1299 Polar Explorer
問題概要
2次元平面上の円で表される星がある。
今自分は角度0の位置にいて、角度Zの位置に宇宙船が着陸した。
宇宙船まで燃費5マイル/ガロンの車で行き、戻ってきたい。
星の半径がX,車のガソリンの燃料がYガロンのとき、宇宙船まで行って戻ってこられるなら余るガソリンの量を、それが不可能ならガソリンで運転できる最大の距離を出力せよ。
解法
解法なんぞ書きようがないぞ……
わからない場合小学3年生だか4年生の頃の教科書を参照。
必要なのは、割る量、割られる量、商の意味の関係。
それから、扇形の弧長の計算方法。
もしくはプログラミング言語がわかっていない可能性があるので、各種入門書or入門サイトを参照。
ソースコード
int main() { char dmy[99]; while(scanf("%s",dmy),strlen(dmy)==5) { int x,y,z; scanf("%d%d%d",&x,&y,&z); z=min(z,360-z); double d=z*3.14159/360*2*x; if(2*d>y*5)printf("NO %d\n",y*5); else printf("YES %d\n",int(y-2*d/5)); scanf("%s",dmy); } return 0; }
1703 Find them, Catch them
問題概要
街にはGang DragonおよびGang Snakeの二つのギャング組織がある。
n人のギャングがおり、そのうちのいくつかの関係が
"A Bの属するギャングは異なる"という形式で与えられる。
その時点で
"A Bは同じギャングに属するか"というクエリを処理せよ。
出力は、
- A Bが同じギャングに属する
- 違うギャングに属する
- その時点では判別できない
の3種類とする。
解法
Union-Findの応用。WA出しまくってたので、komiya先生の解法を参考にさせてもらった。
根と自分が違う符号の場合、違う組織にいることを表す。
ソースコード
int n,p[100001]; int root(int x) { if(abs(p[x])==x)return x; return p[x]=root(abs(p[x]))*(p[x]>0?1:-1); } int main() { int cs; scanf("%d",&cs); while(cs--) { int m; scanf("%d%d",&n,&m); rep(i,n+1)p[i]=i; rep(i,m) { char c; int a,b; scanf(" %c%d%d",&c,&a,&b); if(c=='A') { int pa=root(a),pb=root(b); if(pa!=pb&&pa!=-pb)puts("Not sure yet."); else if(pa!=pb)puts("In different gangs."); else puts("In the same gang."); continue; } int pa=root(a),pb=root(b); if(pa==pb||pa==-pb)continue; p[abs(pa)]=pa>0?-pb:pb; } } return 0; }
2272 Bullseye
問題概要
図(略)のようなダーツの的が与えられる。
的の中心は(0,0)であり、各リングの幅は3である。
投げられたダーツの刺さった座標が与えられるとき、プレイヤーの得点を計算せよ。
解法
原点からの距離を求め、どのリングに刺さったか判定する。
境界の場合は内側に刺さったことになるので、EPSを使ってその辺は調整。
ソースコード
int po(double x,double y) { int r=(int)(hypot(x,y)/3-EPS); return max(100-20*r,0); } int main() { double x,y; while(scanf("%lf%lf",&x,&y),x!=-100) { int a=po(x,y); rep(i,2)scanf("%lf%lf",&x,&y),a+=po(x,y); int b=0; rep(i,3)scanf("%lf%lf",&x,&y),b+=po(x,y); if(a==b)printf("SCORE: %d to %d, TIE. \n",a,b); else printf("SCORE: %d to %d, PLAYER %d WINS.\n",a,b,a>b?1:2); } return 0; }
2812 Extrusion
問題概要
角柱の、底面の形および体積が与えられる。
このとき、角柱の高さを求めよ。
底面は各頂点の座標を時計回りの順で与えられる。
解法
高さ×底面積=体積
多角形の面積はベクトルのクロス積を使う定石が存在。
ソースコード
spaghetti sourceの多角形の面積(http://www.prefield.com/algorithm/geometry/area2.html)を使用。
int main() { int n; while(scanf("%d",&n),n>=3) { G g; rep(i,n) { double x,y; scanf("%lf%lf",&x,&y); g.pb(P(x,y)); } double v; scanf("%lf",&v); printf("BAR LENGTH: %.2f\n",-v/area2(g)*2); } return 0; }