PKU演習問メモ(4/6)

No. Title 分類・解法
1026 Cipher Simple Math, String manipulation
2231 Moo Volume Simple Math
3299 Humidex Bisection method
3278 Catch That Cow BFS
3268 Silver Cow Party Graph, Dijkstra's algorithm
3224 Go for Lab Cup! Straight forward
1555 Polynomial Showdown Straight forward,String manipulation


以下問題概要、解説、ソースコードなど

1026 Cipher

問題概要

相違なる自然数1...nを鍵a[i]とした次のような暗号化を考える。
平文sのi番目の文字がa[i]番目にある文字列をf(s)とする。
fをk回適用した文字列を暗号文とする。
与えられた文字列、n, kに対して対応する暗号文を求めよ。

解法

問題文にkの範囲が書かれてないが、TLE率が高いことを見るとナイーブにやるとTLEしそう。(試してない)
暗号化をk回適用すると、i番目の文字はi→a[i]→a[ a[i] ]→……→i
となっていつかiに戻ってくることを利用する。すなわちそれぞれのiについて周期を求め、kをその周期で割った余りの回数だけ(その文字に)暗号化の操作をすればよい。

ソースコード
int n,ky[200],k;
string buf;
int f[200];
bool u[200];

int rec(int c,int v)
{
	u[c]=1;
	if(v)f[c]=v;
	
	if(u[ky[c]])return 1;
	return 1+rec(ky[c],v);
}
int getp(int c,int r)
{
	if(r<1)return c;
	return getp(ky[c],r-1);
}

int main()
{
	while(cin>>n,n)
	{
		rep(i,n)cin>>ky[i],ky[i]--;
		
		while(cin>>k,k)
		{
			cin.ignore();
			getline(cin,buf);
			int l=buf.size();
			if(l<n)
			{
				buf.resize(n);
				fill(buf.begin()+l,buf.end(),' ');
			}
			
			fill(u,u+n,0);
			rep(i,n)if(!u[i])f[i]=rec(i,0);
			
			fill(u,u+n,0);
			rep(i,n)if(!u[i])rec(i,f[i]);
			
			string ans(n,' ');
			rp(i,buf)ans[getp(i,k%f[i])]=buf[i];
			
			cout<<ans<<endl;
		}
		cout<<endl;
	}
	return 0;
}

2231 Moo Volume

問題概要

数直線上にN個の牛が並んでいて、牛は他のN-1匹の牛に、その距離に等しい大きさの声で
話しかける。Nと牛の座標が与えられた時、全ての牛の声の大きさの総和(N*(N-1)個の和)を求めよ。

解法

TLE率が高いのでO(n^2)でやるとTLEしそう。(試s(ry
左から順に牛と牛の間隔を見て行った時、k番目の区間は何回加算されるかを考えてみる。k番目の区間の左側の牛から右側の牛にそれぞれ一回ずつ、右側から左側にも一回ずつ呼びかけがなされるため、結局、
(左の牛)*(右の牛)*2回だけ加算されることがわかる。


あとは32bit整数に答えが入りきらない可能性があることに注意すればおk.

ソースコード
int n,loc[50000];

int main()
{
	cin>>n;
	rep(i,n)cin>>loc[i];
	
	sort(loc,loc+n);
	ll ans=0;
	
	rep(i,n-1)ans+=2LL*(i+1)*(n-1-i)*(loc[i+1]-loc[i]);
	cout<<ans<<endl;
	
	return 0;
}

3299 Humidex

問題概要

次の式で表されるhumidexという指数がある。

humidex = temperature + h
h = (0.5555)× (e - 10.0)
e = 6.11 × exp [5417.7530 × ((1/273.16) - (1/(dewpoint+273.16)))]


humidex,temperature,dewpointのうちいずれか二つが与えられる。残りの一つを求めよ。

解法

dが与えられればh,tは足し算引き算で求まる。t,hが与えられた場合はdを二分法により求める。
ちなみにhumidexとはカナダで使われる不快指数のような指数。dew pointは露点。

3278 Catch That Cow

問題概要

数直線上をxからx+1,x-1,2*xのいずれかに移動することができる。
初期座標とゴールの座標が与えられた時、初期座標からゴールまで到達するために必要な最小の移動回数を求めよ。いずれの座標も0以上100000以下である。

解法

幅優先探索。移動回数は最大でも|n-k|あれば済むので、その範囲を超えるような移動は考えなくて良い。

ソースコード
int n,k,mx,mxc;
int c[200001];

int main()
{
	cin>>n>>k;
	mxc=abs(n-k); mx=max(n,k)+mxc;
	fill(c,c+mx+1,inf);
	
	queue<int> Q; Q.push(n);
	c[n]=0;
	
	while(!Q.empty())
	{
		int cur=Q.front(); Q.pop();
		
		if(cur==k)
		{
			cout<<c[k]<<endl; break;
		}
		if(c[cur]>=mxc)continue;
		
		for(int d=-1;d<=1;d+=2)
		{
			int nx=cur+d;
			if(nx<0||nx>mx||c[nx]<=c[cur]+1)continue;
			c[nx]=c[cur]+1;
			Q.push(nx);
		}
		if(2*cur<=mx&&c[2*cur]>c[cur]+1)
		{
			c[2*cur]=c[cur]+1;
			Q.push(2*cur);
		}
	}
	
	return 0;
}

3268 Silver Cow Party

問題概要

n個の牧場のうち二つを結ぶ一方通行の道路がm本ある。牧場xでパーティが行われる。全ての牧場にの中で、牧場xまで行き自分の牧場まで戻るのにかかる最短の移動距離のうちで最も大きいものを求めよ。
(n≦1000, m≦100000とする)

解法

nがでかいがワーシャルフロイド法で行けるかと思ったら無理だった。ですよね。


グラフが疎である(n^2に対してmが小さい)ため、O(n^3)のワーシャルフロイドよりもO(E log V)のダイクストラ法をV回繰り返したほうが有利になる。
というわけでダイクストラ法をn回回す。これで1400MS弱となって一応Acceptedなのだが上位の解答は0MS……なんか根本的に間違った方針なのかもしれない。

ソースコード
int d[1000][1000];

int main()
{
	int n,m,x; cin>>n>>m>>x; x--;
	
	rep(i,n)fill(d[i],d[i]+n,inf);
	
	vector<vector<pi> > w(n);
	rep(i,m)
	{
		int s,t,c; cin>>s>>t>>c;
		w[s-1].pb(mp(c,t-1));
	}
	
	rep(i,n)
	{
		int vis=0;
		priority_queue<pi> Q; Q.push(mp(0,i));
		
		while(!Q.empty())
		{
			int cur=Q.top().second,c=Q.top().first;
			Q.pop();
			
			if(d[i][cur]<=-c)continue;
			vis++; d[i][cur]=-c;
			if(vis==n)continue;
			
			fr(iter,w[cur])
			{
				if(d[i][iter->second]>-c+iter->first)
				Q.push(mp(c-iter->first,iter->second));
			}
		}
	}
	
	int ans=0;
	rep(i,n)ans=max(ans,d[i][x]+d[x][i]);
	cout<<ans<<endl;
	
	return 0;
}

3224 Go for Lab Cup!

問題概要

卓球のリーグ戦のスコア表がある。a[i][j]はiがjに対して取った得点で、試合は3点先取である。勝った試合の最も多い先取を求めよ。複数いる場合は番号の若いものを答えよ。

解法

勝った試合の数を数えて最大値をもつ先取を答える。

1555 Polynomial Showdown

問題概要

9つの整数が与えられる。
1番目の数をx^8の係数に、2番目の数をx^7の係数に…9番目の数を定数項にもつような多項式を定められた書式で出力せよ。

解法

定数項、x^1の項、最も次数の大きい項、係数の絶対値が1、すべて0などの場合分けをして出力。


while(!cin.eof())としたらジャッジデータの最後に空行があってハマった……
C++にはJavaのScanner#hasNextIntに相当するようなものはないんだろうか。

ソースコード
int main()
{
	int c[9];
	while(cin>>c[0])
	{
		bool z=c[0]==0; //all zero?
		rep(i,8)
		{
			cin>>c[i+1];
			if(c[i+1])z=0;
		}
		if(z)
		{
			cout<<0<<endl; continue;
		}
		
		bool f=1; //first term?
		rep(i,9)if(c[i])
		{
			if(!f)cout<<" "<<(c[i]>0?"+ ":"- ");
			else
			{
				cout<<(c[i]<0?"-":"");
				f=0;
			}
			if(i==8||abs(c[i])!=1)cout<<abs(c[i]);
			if(i!=8)cout<<"x";
			if(i<7)cout<<"^"<<8-i;
		}
		cout<<endl;
	}
	return 0;
}