Particle

競技プログラミングについての雑記

KUPC2012

(感想)難易度が丁度良かったから、5時間ずっと楽しめました。

A.
x*(t/x)でxの倍数でt以下の最大の整数を求めると、TLEしません。

int main(){
	int n,t,e;
	cin>>n>>t>>e;
	for(int i = 1; i <= n; i++){
		int x;
		cin>>x;
		int y = x*(t/x);
		if(t-e<=y||y+x<=t+e){
			cout<<i<<endl;
			return 0;
		}
	}
	cout<<-1<<endl;
	return 0;
}

B.
両端が同じ色のとき、その色のプレーヤーが勝ちます。
それ以外のときは、白が両端を白にするので先手が勝ちます。

int main(){
	string s;
	cin>>s;
	if(s[0]==s[s.size()-1])cout<<s[0]<<endl;
	else cout<<'o'<<endl;
	return 0;
}

C.
頑張って実装する。自分は、嫌いなペアがあるかどうかを考えました。

int n,k,r,boat[50][50],ab[50];
vector<int> ng[50];

int main(){
	cin>>n>>k;
	for(int i = 0; i < k; i++){
		int m;
		cin>>m;
		for(int j = 0; j < m; j++){
			int bunny;
			cin>>bunny;
			boat[i][bunny-1]++;
		}
	}
	cin>>r;
	for(int i = 0; i < r; i++){
		int b1,b2;
		cin>>b1>>b2;
		b1--;b2--;
		ng[b1].push_back(b2);
		ng[b2].push_back(b1);
	}
	for(int i = 0; i < n; i++){
		if(ab[i]||ng[i].empty())continue;
		int l = ng[i].size();
		for(int j = 0; j < l; j++){
			int t = ng[i][j];
			for(int j2 = 0; j2 < k; j2++){
				if(boat[j2][i]&&boat[j2][t]){
					ab[i]=ab[t]=1;
					break;
				}
			}
		}
	}
	int ans = 0;
	for(int i = 0; i < n; i++)ans+=ab[i];
	cout<<ans<<endl;
	return 0;
}

D.
貪欲法で解けます。
左の部屋から見ていき、左端をカバーしつつ、出来るだけ右までカバーできる教授を見つけ出します。見付け出さなければ"Impossible"を出力します。

vector<pair<int,int> > d;
int n,m;

int main(){
	cin>>n>>m;
	for(int i = 0; i < m; i++){
		int a,b;
		cin>>a>>b;
		d.push_back(make_pair(b,a));
	}
	sort(d.begin(),d.end(),greater<pair<int,int> >());
	int pos = 1,res = 0;
	while(pos<=n){
		for(int i = 0; i <= m; i++){
			if(i==m){
				cout<<"Impossible"<<endl;
				return 0;
			}
			int b = d[i].first,a = d[i].second;
			if(a<=pos&&pos<=b){
				res++;
				pos = b+1;
				break;
			}
		}
	}
	cout<<res<<endl;
	return 0;
}

E.
これくらい酷いコードでも、少しずつ変えて何回もsubmitすればいつかは通ると思います。

int n,g[10][10],ai[10],s[10],t[10],put[10],point,win[10];

void update(){
	memset(s,0,sizeof(s));memset(t,0,sizeof(t));
	for(int i = 0; i < n; i++){
		for(int j = 0; j < n; j++){
			s[i]+=(g[i][j]*ai[j]);
			t[j]-=(g[i][j]*ai[i]);
		}
	}
}

void decide(){
	for(int i = 0; i < 10; i++){
		int a = rand()%n;
		int b = rand()%n;
		if(i%2){
			put[i]=s[a]>s[b]?a:b;
		}
		else{
			put[i]=t[a]>t[b]?a:b;
		}
	}
	random_shuffle(put,put+n);
}

int main(){
	srand(time(NULL));
	for(int i = 0; i < 10; i++)ai[i]++;
	cin>>n;
	for(int i = 0; i < n; i++){
		for(int j = 0; j < n; j++){
			char c;
			scanf(" %c",&c);
			if(c=='o')g[i][j]=1;
			if(c=='x')g[i][j]=-1;
		}
	}
	for(int i = 0; i < 10; i++){
		int hand;
		printf("%d\n",rand()%n+1); fflush(stdout);
		scanf("%d", &hand);
		ai[hand-1]+=2;
	}
	update();
	int e = 99;
	for(int c = 0; c < 99; c++){
		if(point<-250){
			e = c;
			break;
		}
		decide();
		for(int i = 0; i < 10; i++){
			int hand;
			printf("%d\n",put[i]+1); fflush(stdout);
			scanf("%d", &hand);
			point+=g[put[i]][hand-1];
			if(g[put[i]][hand-1]>=0){
				win[put[i]]++;
			}
			ai[hand-1]++;
		}
		for(int i = 0; i < 10; i++){
			ai[i] *= 0.9;
			win[i] *= 0.6;
		}
		update();
	}
	for(int c = e; c < 99; c++){
		for(int i = 0; i < 10; i++){
			int hand;
			printf("%d\n",5%n+1); fflush(stdout);
			scanf("%d", &hand);
		}
	}
	
	return 0;
}

F.
シミュレーションで部分点を貰おうとする → RE

G.
UnionFind木を使うと解けます。

x軸か、y軸かでソートすると、枝刈りできますが、それぞれに強いデータが入っているので工夫が必要です。
x座標とy座標のそれぞれで分散を計算して、それが大きい方を基準に枝刈りするようにしたら通りました。

#define x first
#define y second

#define MAX_N 200000
	int par[MAX_N];
	bool united[MAX_N];
struct UF {
	void init(int n){
		for(int i = 0; i < n; i++){
			par[i] = i;
		}
	}
	
	int find(int x){
		if(par[x] == x){
			return x;
		}else{
			return par[x] = find(par[x]);
		}
	}
	
	void unite(int x, int y){
		united[y] = 1;
		x = find(x);
		y = find(y);
		if(x == y) return;
		par[x] = y;
	}
	
};

int N;
double R;
pair<double,double> p[MAX_N];
const double EPS = 1e-9;

double dist(int a,int b){
	return (p[a].x-p[b].x)*(p[a].x-p[b].x)+(p[a].y-p[b].y)*(p[a].y-p[b].y);
}

double distx(int a,int b){
	return (p[a].x-p[b].x)*(p[a].x-p[b].x);
}

void var(){
	double avex=0,avey=0;
	for(int i = 0; i < N; i++){
		avex+=p[i].x;avey+=p[i].y;
	}
	avex/=N;avey/=N;
	double sx=0,sy=0;
	for(int i = 0; i < N; i++){
		sx+=(avex-p[i].x)*(avex-p[i].x);
		sy+=(avey-p[i].y)*(avey-p[i].y);
	}
	if(sx<sy){
		for(int i = 0; i < N; i++){
			swap(p[i].x,p[i].y);
		}
	}
}

int main(){
	cin>>N>>R;
	for(int i = 0; i < N; i++)cin>>p[i].x>>p[i].y;
	var();
	UF uf;
	uf.init(N);
	sort(p,p+N);
	for(int i = 0; i < N; i++){
		if(!united[i]){
			for(int j = i+1; j < N; j++){
				if(distx(i,j)>R*R+EPS)break;
				if(dist(i,j)<=R*R+EPS){
					uf.unite(i,j);
				}
			}
		}
	}
	int res = 0;
	for(int i = 0; i < N; i++){
		if(i==uf.find(i))res++;
	}
	cout<<res<<endl;
	return 0;
}

H.
ある点の縦と横にある0の個数の奇数である点の縦横にある点の偶奇を入れ替えると、全て奇数になります。証明はまだ思いついていません。

追記:
正しいかわからないけど、一応考えてみた

ある点(a,b)の縦横の列とその点にある0の個数を2で割ったものをf(a,b)とする。
h,wは偶数であるため、ある点(a,b)とそれの縦横の列にある数字の偶奇を入れ替えると、f(a,b)だけ入れ替わって、他は入れ替わらない。
状態が2^(h*w)通りあるので、
∀a,b f(a,b) = 0
となるのは、全てのマスが1であるときだけであることが分かる (q.e.d.)

また、このことから任意の状態から、全てのマスを1にすることができ、出力は1通りしか無いことが分かる。

int h,w,g[1000][1000],gi[1000],gj[1000],gij[1000][1000];

int main(){
	scanf("%d%d",&h,&w);
	for(int i = 0; i < h; i++){
		for(int j = 0; j < w; j++){
			scanf("%d",g[i]+j);
			g[i][j] = 1-g[i][j];
		}
	}
	for(int i = 0; i < h; i++){
		for(int j = 0; j < w; j++){
			gi[i]+=g[i][j];
			gj[j]+=g[i][j];
		}
	}
	for(int i = 0; i < h; i++){
		for(int j = 0; j < w; j++){
			gij[i][j] = gi[i]+gj[j]-g[i][j];
		}
	}
	for(int i = 0; i < h; i++){
		for(int j = 0; j < w; j++){
			printf("%d",gij[i][j]&1);
			if(j!=w-1)printf(" ");
		}
		printf("\n");
	}
	return 0;
}


37thでした。