Hatena::Grouptopcoder

yuyarinのtopcoder記

TopCoder, Google Code JamPKU JudgeOnlineICPC などのアルゴリズム系プログラミングコンテストの参加や練習の記録を残していきます.

アルゴリズムやテーマで分類した目次はこちら

2010/05/28

TopCoder Open 2010 Algorithm Qualification Round 3 SumRectangle

| 16:31

http://www.topcoder.com/stat?c=problem_statement&pm=10948&rd=14278

問題

二次元の表の一番左の列と一番上の行の値が与えられている.あるセルの値は下と右下と右のセルの和になっている.表の一番右下の値を求める.

アプローチ

特に工夫は必要なし.

ソースコード

#include <cstdio>
#include <string>
#include <vector>
#include <iostream>
#include <sstream>

#define Forall(i,v)   for(int i=0;i<(int)v.size();++i)
#define For(i,a,b)    for(int i=(a);i<(b);++i)
#define Rep(i,n)      for(int i=0;i<(n);++i)

#define sz(a) int((a).size())

using namespace std;

int m[10][10];

int getCorner(vector <int> leftColumn, vector <int> topRow)
{
	int nl = leftColumn;
	int nt = topRow;
	
	Rep(j,nl)
		m[j][0] = leftColumn[j];
	Rep(i,nt)
		m[0][i] = topRow[i];
	
	For(j,1,nl) For(i,1,nt)
		m[j][i] = m[j-1][i-1] - m[j][i-1] - m[j-1][i];
	
	return m[nl-1][nt-1];
}

TopCoder Open 2010 Algorithm Qualification Round 3 WhatsThisChord

| 16:31

http://www.topcoder.com/stat?c=problem_statement&pm=10927&rd=14278

問題

ギターの弦を押さえる時にどのコードになるのかを求める問題.6つの弦のどのフレットを押さえるのかが数値で与えられる.特に 0 なら開放弦,-1 なら弾かない,である.どの音の Major か Minor なのかを判定する.

アプローチ

全探索でも全く問題ないが,コードが汚くなる.

とりあえず押さえる弦を配列に突っ込んで 12 の剰余を取って unique をかけて正規化する(*1).ここで要素が 3 つでなければ終了.3 つの場合,その後ろにそれぞれ 12 を足した値を追加しておくと(*2)後のコード判定がすっきりして楽になる(*3).

ソースコード

#include <cstdio>
#include <string>
#include <vector>
#include <iostream>
#include <sstream>

using namespace std;

typedef vector<int> VI;

#define pb push_back
#define sz(a) int((a).size())

#define For(i,a,b)    for(int i=(a);i<(b);++i)
#define Rep(i,n)      for(int i=0;i<(n);++i)

#define Unique(v) \
	sort((v).begin(),(v).end());\
	v.resize(unique((v).begin(),(v).end())-(v).begin());
	
int s[6] = {4,9,2,7,11,4};
string name[12] = {"C","C#","D","D#","E","F","F#","G","G#","A","A#","B"};

string classify(vector<int> chord)
{
	VI c;
	Rep(i,6) if(chord[i]>=0)
		c.pb((chord[i]+s[i])%12);
	// (*1)
	Unique(c);
	
	if(sz(c)!=3)
		return "";
	
	// (*2)
	Rep(i,3)
		c.pb(c[i]+12);
	
	// (*3)
	Rep(i,3)
	{
		if(c[i+1]-c[i]==4 && c[i+2]-c[i+1]==3)
			return name[c[i]]+" Major";
		if(c[i+1]-c[i]==3 && c[i+2]-c[i+1]==4)
			return name[c[i]]+" Minor";
	}
	
	return "";
}

TopCoder Open 2010 Algorithm Qualification Round 3

| 16:31

http://www.topcoder.com/stat?c=problem_statement&pm=10929&rd=14278

問題

横 W 縦 H の芝生をダイアモンドカッターが切っていく.カッターの初期位置と,移動方向のプログラムが与えられたときに,芝生が何分割されているかを答える.

アプローチ

ある位置の芝生の左が切れているかどうかのフラグと上が切れているかどうかのフラグを用意しておき,プログラムを走らせて切っていき,最後に領域を数える.

領域を数えるのは再帰(スタック,深さ優先)だと最悪 10^6 になるのでオーバーフローする.queue を使って幅優先探索を行う.

ソースコード

#include <cstdio>
#include <string>
#include <vector>
#include <iostream>
#include <sstream>
#include <queue>

using namespace std;

typedef pair<int,int> PII;

#define sz(a) int((a).size())
#define pb push_back
#define mp make_pair
#define Forall(i,v)   for(int i=0;i<(int)v.size();++i)
#define For(i,a,b)    for(int i=(a);i<(b);++i)
#define Rep(i,n)      for(int i=0;i<(n);++i)

bool cutu[1024][1024];
bool cutl[1024][1024];
bool glass[1024][1024];

queue<PII> q;

int pieces(int W, int H, int startx, int starty, vector <string> program)
{
	Rep(j,H+1) Rep(i,W+1)
		cutu[j][i] = cutl[j][i] = glass[j][i] = false;
	Rep(i,W+1)
		cutu[0][i] = cutu[H][i] = true;
	Rep(j,H+1)
		cutl[j][0] = cutl[j][W] = true;
	
	int x = startx;
	int y = starty;
	
	Rep(pp,sz(program)) Rep(p,sz(program[pp]))
	{
		switch(program[pp][p])
		{
		case 'U':
			cutl[--y][x] = true;
			break;
		case 'D':
			cutl[y++][x] = true;
			break;
		case 'L':
			cutu[y][--x] = true;
			break;
		case 'R':
			cutu[y][x++] = true;
			break;
		}
	}
	
	int c = 0;
	Rep(j,H) Rep(i,W) if(!glass[j][i])
	{
		c++;
		q.push(mp(i,j));
		while(!q.empty())
		{
			PII co = q.front(); q.pop();
			int x = co.first;
			int y = co.second;
			if(glass[y][x])
				continue;
			glass[y][x] = true;
			if(!cutu[y][x])   q.push(mp(x,y-1));
			if(!cutu[y+1][x]) q.push(mp(x,y+1));
			if(!cutl[y][x])   q.push(mp(x-1,y));
			if(!cutl[y][x+1]) q.push(mp(x+1,y));
		}
	}
	
	return c;
}

DiegoDiego2012/07/10 00:56The pucrahses I make are entirely based on these articles.

rcomsiercomsie2012/07/10 15:55WkGRgf <a href="http://ndjqpxpaabuy.com/">ndjqpxpaabuy</a>

lurhlpgihxdlurhlpgihxd2012/07/10 21:512sAKJD , [url=http://wyqyxabtrpox.com/]wyqyxabtrpox[/url], [link=http://fetlavsdhbsq.com/]fetlavsdhbsq[/link], http://frjyegfwmtje.com/

sxyqsmsxyqsm2012/07/12 12:11UCCj2u <a href="http://srlypizeryby.com/">srlypizeryby</a>

2010/05/18

TopCoder Open 2010 Algorithm Qualification Round 2 HandlesSpelling

| 19:40

http://www.topcoder.com/stat?c=problem_statement&pm=10864&rd=14277

System Test: Passed

長い文字列(parts)を,短い文字列(badges)で覆っていったとき(同じbadgeを何度でも使える),覆った部分が繋がる最長の長さ A と,覆えなかった文字の数 B について, A^2 - B の最大値を求める問題.

Div 1, Div 2 を通して 1000 pt. の問題が自力で解けたのは初めて.TopCoderDP の問題を解いたのも初めてだと思う.それにしてもこの問題は DP の問題でもかなり難易度が高いと思う.

赤コーダーのコードを読んでみたけどさっぱりわからない...けど配列の数を見たりすると,僕のはなんとなく効率が悪いアルゴリズムのようだ.

解説を入れてみたので長くなるので表示省略↓


parts は最大 50 文字が最大 20 要素なので 1000 文字.badges は最大 50 文字が最大 50 要素なので,総当たりだと最大 2^50 の計算量.

なのでお決まりの DP で解く.

ひとまず parts を全部つなげた文字列 s を考えるとして,分割方法としては s の長さを短くするのと,badges の数を減らすのが考えられるが,文字列マッチのDP問題では前者の方法が一般的?

なので覆うべき s を先頭 0 文字から順に増やしていくことにする.

記録する値を考える.DP では縮小した問題の結果を利用できなくてはいけないので A^2 - B をそのまま記録してはいけない.s[0..i] で最大を取ったパターンを継承したものが s[0..i+m] で最大を取るとは限らないからだ.

0123456789
ABCDEFGHIJ (s)
ABC        (badges[0])
  CD       (badges[1])
    EFGHIJ (badges[2])

この例を考えたときに s[0..3] の最大のパターンは badges[0] だけを使った時の 3^2 -1 = 8 である.s[0..9] は s[0..3] + s[4..9] で badges[2] がちょうど s[4..9] にマッチするので,badges[2] を加えれば s[0..9] の時の最大のパターンが求まると思うが,badges[1] は badges[2] と繋がるので,こちらを選択した方が良い結果になる.

では,なるべく次に繋がるように選んでいけばよいのか? s[0..3] までを見たときに badges[0] ではなく次に繋がりそうな badges[1] を選ぶことにしよう.実は s が s[3] までしかなかったという場合に失敗するのは明らかであるし,以下のような場合でも失敗する.

0123456789
ABCDEFGHIJ (s)
ABC        (badges[0])
  CD       (badges[1])
     FGHIJ (badges[2])

また,次のようなケースを考えると s[0..5] までは badges[0] を取った方がいいけど,s[0..9] まで拡大すると badges[2] が選択可能になるので badges[1] を選択したパターンを予め考慮に入れていないと正しい答えが出せない.

また,s[0..3] で badges[1] と badges[3] を取っていれば,連続する長さが 1 でも覆えない文字の数は減らせるが,後でそれを変更できなければ badges[2] が選択可能になっても badges[3] と衝突して選択できなくなるので,連続する長さが短くて,たくさん覆えないパターンも生き残らせる必要がある.

0123456789
ABCDEFGHIJ (s)
ABCDEF     (badges[0])
 B         (badges[1])
  CDEFGHIJ (badges[2])
   D       (badges[3])

同じ最大長を持っていて,次に繋がる可能性があるパターンが2つあったときにどうするかが問題になる.次に繋がる可能性が「長い」パターンを選んでしまうと次のようなケースで失敗する.多く覆っているパターンを選択すると,次の例で badges[3] が無いパターンで失敗する.

0123456789
ABCDEFGHIJKLMN.... (s)
ABCDEFGH           (badges[0])
         J         (badges[1])
  CDEFGHIJ         (badges[2])
           L...    (badges[3])

なので次に繋がる可能性が長いパターンと,多くを覆っているパターンの両方を選択できるようにする.

次に繋がる可能性が一番長いというわけでもなく,一番多く覆っているというわけでもないパターンは,絶対に最良のケースには成り得ないのでこれで十分である.なぜなら badge が続く場合は,A^2 が効いて繋がる可能性が一番長いものの方が有利になるし,続いた上で可能性が一番長いものですら最長にならない場合は,多く覆っている方が有利になるし, badge が後に続かない場合は,一番多く覆っている方が有利である.

まとめると

  • これまでに連続した長さが長い方がいい
  • 同じ長さが連続しているなら,たくさん覆っている方がいい
  • 連続する長さが短くてたくさん覆えなくても最終的に選択される可能性がある
  • 次に繋がる可能性は残しておく必要がある
  • 次に繋がる可能性がある場合は,これまでに一番多く覆っているパターンと,これから一番長く続きそうなパターンを残しておく

これを踏まえると,

  1. これまでに連続している長さによって記録する場所を分けた方がいい
  2. 次に繋がる可能性があるかどうかで記録する場所を分けた方がいい
  3. 次に繋がる可能性がある場合は多く覆っているパターンを選んだ記録と,長く続きそうなパターンを選んだ記録を残す

1. によって A^2 -B のうちパラメータ A は分離されるので B に関する値だけ,つまり覆っていない文字の数を記録していけばよい.この配列を uncoverd という名前にする.

#define LIM 1024
int uncoverd[LIM][LIM][3];
// uncoverd[i][j][k]

i は s を 0..i に縮小する場合を考えるインデックスである.i=0〜1000 なので大きさは 1024 で十分である.

j はこれまでに連続している長さによって記録をわけるためのインデックスである.j 文字連続して覆えている場合は uncoverd[i][j] に記録する.

k は配列の種類を表すインデックスである.

  1. 次に繋がる可能性がない場合 (k=0)
  2. 次に繋がる可能性があり,多く覆っているパターンを選ぶ場合 (k=1)
  3. 次に繋がる可能性があり,長く続きそうなパターンを選ぶ場合 (k=2)

はそれぞれ別の配列を用意してもいいが,処理が共通するのでループで回す方が簡潔に書けるため,このようにした.

こうすれば j^2 - uncoverd[s.length()][j][k] が最大になるものを最後に計算すれば答えが出る.

uncoverd[0][0][k] は 0 で初期化できる.対象が 0 文字なので覆っていない文字の数は必ず 0 である.他は大きな値で初期化する.覆えていない文字数なので最大値は 1000 である.よって 1024 で初期化することにする.

さて,覆うべき文字が後ろに一文字増えたときには

  1. 覆えない文字が1文字増える
  2. その文字から覆いが始まる badge がある
  3. その文字で覆いが終わる badge がある

ということが起きる.3 に関しては,終わるのであれば始まりがあるので 2 の時に処理してしまえばいい.このときある (i,j,k) について,覆い終わりなのか,覆い終わりであれば覆いが何文字つづいているのか,という情報が必要になる.k=0 の時は定義上覆い終わりには成り得ないのでこれを抜き int tail[LIM][LIM][2]; という配列を用意して,何文字続いて覆い終わったのかを記録する.これは 0 ですべて初期化できる.

int tail[LIM][LIM][2];
// tail[i][j][k-1]
// k-1=0 が uncoverd の k=1 に対応する

ここまで準備できればあとは簡単である.

http://www.yuyarin.net/screenshot/20100518010601.png

↑デバッグ中,System Test にあるテストケースについての uncoverd[i][j][0] の一部.

uncoverd[i+1][j][0] は uncoverd[i][j][k]+1 (k=0,1,2) のうち最小のものになる.これは覆うべき文字が 1 つ増えたときに覆わない選択を記録するものである.

次に,ある (i,j,k) について i から覆い始まる badge がみつかった時の処理である.その badge の長さを m とすると,この badge を選択したときに,連続して覆うことができる最長の長さは,tail[i][j][k-1] + m と j の大きい方となる.tail[i][j][k-1] + m は新しい badge を選んだときに,最長の長さが増える場合,j は増えない場合を意味する.これを x とする.k==0 のときは tail[i][j][k-1] を加えてはいけない.

int c = m + (k?tail[i][j][k-1]:0);
int x = max(j, c);

この badge を選択すると m 個後に x 文字連続して覆えている uncoverd[i+m][x][k] と tail[i+m][x][k-1] が影響を受ける.覆えていない文字は増えないので uncoverd[i+m][x][k] は uncoverd[i][x][k] と同じになる.tail[i+m][x][k-1] は tail[i][j][k-1] + m = c になる.

多く覆っているパターンを選ぶ場合(k==1)は

// 覆っている数が多くなる
if((uncoverd[i+m][x][1]>uncoverd[i][j][k])
// 同じだったら長く続きそうな方
|| (uncoverd[i+m][x][1]==uncoverd[i][j][k]&&tail[i+m][x][0]<c))
{
	uncoverd[i+m][x][1] = uncoverd[i][j][k];
	tail[i+m][x][0] = c;
}

長く続きそうなパターンを選ぶ場合(k==2)は

// 長く続く
if((tail[i+m][x][1]<c)
// 同じだったら多く覆えている方
||(tail[i+m][x][1]==c&&uncoverd[i+m][x][2]>uncoverd[i][j][k]))
{
	uncoverd[i+m][x][2] = uncoverd[i][j][k];
	tail[i+m][x][1] = c;
}

以上を纏めるとこのようになる

#include <cstdio>
#include <string>
#include <vector>
#include <iostream>
#include <sstream>
#include <climits>

#define sz(Z) ((int)Z.size())

using namespace std;

#define LIM 1024

int uncoverd[LIM][LIM][3];
int tail[LIM][LIM][2];
bool match[LIM][50];

class HandlesSpelling
{
public:

int spellIt(vector <string> parts, vector <string> badges)
{
	string s = "";
	for(int i=0;i<sz(parts);i++) s += parts[i];
	
	int n = sz(badges);
	int l = sz(s);
	
	for(int i=0; i<n; i++)
	{
		for(int j=0; j<l; j++)
			match[j][i] = false;
		size_t idx = -1;
		while((idx=s.find(badges[i], idx+1))!=string::npos)
			match[idx][i] = true;
	}
	
	for(int i=0; i<LIM; i++) for(int j=0; j<LIM; j++)
	{
			uncoverd[i][j][0] = uncoverd[i][j][1] = uncoverd[i][j][2] = LIM;
			tail[i][j][0] = tail[i][j][1] = 0;
	}
	
	uncoverd[0][0][0] = uncoverd[0][0][1] = uncoverd[0][0][2] = 0;
	
	for(int i=0; i<l; i++) for(int j=0; j<l; j++) for(int k=0; k<3; k++)
	{
		if(uncoverd[i][j][k]>=LIM) continue;
		
		if(uncoverd[i+1][j][0]>uncoverd[i][j][k]+1)
			uncoverd[i+1][j][0] = uncoverd[i][j][k]+1;
		
		for(int b=0; b<n; b++)
		{
			if(!match[i][b]) continue;
		
			int m = sz(badges[b]);
			int c = m + (k?tail[i][j][k-1]:0);
			int x = max(j, c);
			if((uncoverd[i+m][x][1]>uncoverd[i][j][k])
			|| (uncoverd[i+m][x][1]==uncoverd[i][j][k]&&tail[i+m][x][0]<c))
			{
				uncoverd[i+m][x][1] = uncoverd[i][j][k];
				tail[i+m][x][0] = c;
			}
			if((tail[i+m][x][1]<c)
			||(tail[i+m][x][1]==c&&uncoverd[i+m][x][2]>uncoverd[i][j][k]))
			{
				uncoverd[i+m][x][2] = uncoverd[i][j][k];
				tail[i+m][x][1] = c;
			}
		}
	}
	
	int r = -LIM;
	for(int j=0; j<LIM+1; j++) for(int k=0; k<3; k++)
		if(uncoverd[l][j][k]<LIM)
			if(r<j*j-uncoverd[l][j][k])
				r = j*j-uncoverd[l][j][k];
	return r;
}

LucindaLucinda2011/08/30 17:11That's way more celevr than I was expecting. Thanks!

qcsfbdcrznfqcsfbdcrznf2011/09/01 01:16MuL60l <a href="http://ldwctdptemza.com/">ldwctdptemza</a>

uzzdbpogphuzzdbpogph2011/09/02 21:19Ek7mGB , [url=http://xuvqzogutnji.com/]xuvqzogutnji[/url], [link=http://mtfcrzsqbevx.com/]mtfcrzsqbevx[/link], http://yidsymtdxulp.com/

sjsczxrsjsczxr2011/09/03 18:32v2hpOA <a href="http://bwurazrpzmzh.com/">bwurazrpzmzh</a>

jhwvoumajhwvouma2011/09/04 01:44LhXMUP , [url=http://rjqsugidoqec.com/]rjqsugidoqec[/url], [link=http://gukwrezfgzuo.com/]gukwrezfgzuo[/link], http://zesuvabbctky.com/

2010/05/12

TopCoder Open 2010 Algorithm Qualification Round 2

| 23:48

250 pt. は少し出遅れたけど正解.500 pt. はアルゴリズムは正しいけど計算量の問題があって TLE なケースをチャレンジされて撃墜.

まぁでも 514 位でなんとか予選通過したみたいだし rating も 1203 になってついに青コーダー!!

Problems

Class NameMethod NameDifficultyCoding TimeStatusPoints
JingleRingleprofitLevel One0:15:09.742Passed System Test199.23
FuzzyLifesurvivingCellsLevel Two0:59:02.978Challenge Succeeded198.62

Defence

DefendantChallengerProblemSucceededChallenge TimePoints
yuyarinBolekFuzzyLifeNo05:33.487-198.62

Statistics

Room 16

Coding PhaseChallenge PhaseSystem TestPoint TotalDivision PlacedAdv.Old RatingRating ChangeNew Rating
397.85-198.620.00199.23514N1177261203

http://www.topcoder.com/stat?c=coder_room_stats&rd=14277&rm=304501&cr=22851423

TopCoder Open 2010 Algorithm Qualification Round 2 JingleRingle

| 23:48

http://www.topcoder.com/stat?c=problem_statement&pm=10896&rd=14277

250 pt. 問題.

buyOffer を大きい方から,sellOffer を小さい方から tax を考慮しながら見ていくだけ.

#include <cstdio>
#include <string>
#include <vector>
#include <iostream>
#include <sstream>

using namespace std;

#define sz(a) int((a).size())
#define Sort(c)     sort((c).begin(),(c).end())

int profit(vector <int> b, vector <int> s, int tax)
{
	Sort(b);
	Sort(s);
	
	if(sz(b)<1 || sz(s)<1) return 0;
	
	int bi = sz(b)-1;
	int si = 0;
	int tp = 0;
	
	while(bi>=0 && si<sz(s))
	{
		int p = b[bi] - (b[bi]*tax)/100 - s[si];
		if(p<=0)
			break;
		tp += p;
		bi--;
		si++;
	}
	
	return tp;
}

TopCoder Open 2010 Algorithm Qualification Round 2 FuzzyLife

| 23:48

http://www.topcoder.com/stat?c=problem_statement&pm=10911&rd=14277

500 pt. 問題.

周りのセルも数えることを忘れていたことで時間を取ってしまい,最後 30 秒で Sample を Passed して commit.Challenging Phase で撃墜される.最大のケースで TLE してしまっていたようだ.

再帰によって n 個ある ? のセルを 1 つずつ固定して生存セルが一番多くなるものを選ぶ方法を取っていたのだけど,この方法の計算量は O(2^n) で,最大のケースだと n=272 が可能となり,このスケールになると TLE してしまう.

No cell will have more than one neighboring cell of type '?'

というのがヒントで,これはつまり,ある ? のセルを確定させる時に影響されるセルは他の ? のセルの影響を受けないということである.つまり1つずつ確定(固定ではない)させて行くことができる.これなら O(n) で処理できる.

具体的には ? がでてきたら,その周り 9 マスだけを計算して,多い方を選んで確定していけば十分である.


#include <cstdio>
#include <string>
#include <vector>
#include <iostream>
#include <sstream>

using namespace std;

typedef vector<string> VS;

#define sz(a) int((a).size())

int n;
int m;

class FuzzyLife
{
public:

// grid の特定の範囲だけ計算
int calcRange(VS &g, int mini, int maxi, int minj, int maxj)
{
	int tc = 0;
	for(int i=mini; i<=maxi; i++)
	{
		for(int j=minj; j<=maxj; j++)
		{
			int c = 0;
			if(i>0)
			{
				c += g[i-1][j]-'0';
				if(j>0)
					c += g[i-1][j-1]-'0';
				if(j<m-1)
					c += g[i-1][j+1]-'0';
			}
			if(i<n-1)
			{
				c += g[i+1][j]-'0';
				if(j>0)
					c += g[i+1][j-1]-'0';
				if(j<m-1)
					c += g[i+1][j+1]-'0';
			}
			if(j>0)
				c += g[i][j-1]-'0';
			if(j<m-1)
				c += g[i][j+1]-'0';
			if(g[i][j]=='1')
				if(c==2||c==3) tc++;
			else
				if(c==3) tc++;
		}
	}
	return tc;
}

int survivingCells(vector <string> grid)
{
	n = sz(grid);
	m = (int)grid[0].length();
	
	// 周りに 0 を 1 周分追加
	VS g;
	string s(m+2, '0');
	g.push_back(s);
	for(int i=0; i<n; i++)
		g.push_back("0"+grid[i]+"0");
	g.push_back(s);
	
	n += 2;
	m += 2;
	
	for(int i=0; i<n; i++)
	{
		for(int j=0; j<m; j++)
		{
			if(g[i][j]=='?')
			{
				// ? のセルを固定して,周囲 9 セルだけを計算
				g[i][j] = '0';
				int r0 = calcRange(g, i-1, i+1, j-1, j+1);
				g[i][j] = '1';
				int r1 = calcRange(g, i-1, i+1, j-1, j+1);
				// 確定する
				g[i][j] = (r0>r1) ? '0':'1';
			}
		}
	}
	
	// 全体を計算する
	return calcRange(g, 0, n-1, 0, m-1);
}

AgustinaAgustina2012/07/10 05:30Yo, good loiokn out! Gonna make it work now.

dvmpfhnxpodvmpfhnxpo2012/07/10 16:19M992tq <a href="http://abuptwpwcqym.com/">abuptwpwcqym</a>

pptauxpptaux2012/07/11 20:46FCqabg , [url=http://fihpbpjoyarc.com/]fihpbpjoyarc[/url], [link=http://ciuzxzgiuyqo.com/]ciuzxzgiuyqo[/link], http://lvgsivtbvmij.com/

ktxodmtktxodmt2012/07/12 12:32jofRQ4 <a href="http://kizscxqiqaki.com/">kizscxqiqaki</a>

fncfmwxjlrfncfmwxjlr2012/07/12 18:05oX6byq , [url=http://cgudxnkygdfi.com/]cgudxnkygdfi[/url], [link=http://iuikvqdlnsqu.com/]iuikvqdlnsqu[/link], http://ebjctraukepx.com/