Educational Codeforces Round 49 D Mouse Hunt

前回のままではさすがに芸が無いからsccの応用問題を解いてみたよ
Problem - 1027D - Codeforces
強連結成分分解→強連結成分どうしの隣接リストを作ってdfsの流れで

#include<cstdio>
#include<vector>
#include<algorithm>
#include<climits>
using namespace std;
static const int MAX_N = 200000;

int N;
int c[MAX_N], a[MAX_N];

vector<int> G[MAX_N];
vector<int> rG[MAX_N];
vector<int> vs;
bool used[MAX_N];
int cmp[MAX_N];

vector<int> G2[MAX_N];
int num[MAX_N];
bool visited[MAX_N];

void add_edge(int from, int to){
	G[from].push_back(to);
	rG[to].push_back(from);
}

void dfs(int v){
	used[v] = true;
	for(int i = 0; i < G[v].size(); i++){
		if(!used[G[v][i]]) dfs(G[v][i]);
	}
	vs.push_back(v);
}

void rdfs(int v, int k){
	used[v] = true;
	cmp[v] = k;
	for(int i = 0; i < rG[v].size(); i++){
		if(!used[rG[v][i]]) rdfs(rG[v][i], k);
	}
}

int scc(){
	fill(used, used + N, false);
	vs.clear();
	for(int i = 0; i < N; i++){
		if(!used[i]) dfs(i);
	}
	fill(used, used + N, false);
	int k = 0;
	for(int i = vs.size() - 1; i >= 0; i--){
		if(!used[vs[i]]) rdfs(vs[i], k++);
	}
	return k;
}

int dfs2(int s){
	if(visited[s]) return 0;
	visited[s] = true;
	if(G2[s].size() == 0) return num[s];
	int res = 0;
	for(int i = 0; i < G2[s].size(); i++){
		if(visited[G2[s][i]]) continue;
		res += dfs2(G2[s][i]);
	}
	return res;
}

int main(){
	scanf("%d", &N);
	for(int i = 0; i < N; i++) scanf("%d", &c[i]);
	for(int i = 0; i < N; i++){
		scanf("%d", &a[i]);
		a[i]--;
		if(i == a[i]) continue;
		add_edge(i, a[i]);
	}
	int n = scc();
	fill(num, num + n, INT_MAX);
	fill(visited, visited + n, false);
	for(int i = 0; i < N; i++){
		num[cmp[i]] = min(num[cmp[i]], c[i]);
		if(cmp[i] == cmp[a[i]]) continue;
		G2[cmp[i]].push_back(cmp[a[i]]);
	}
	int res = 0;
	for(int i = 0; i < n; i++){
		res += dfs2(i);
	}
	printf("%d\n", res);
	return 0;
}

Peking University Online Judge 3180 The Cow Prom

・sccを貼っただけ
・全部の関数を使う必要があるのか
・そもそも本当にsccの問題なのか
などの理由で消化不良、本当に実力はついているのか

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
static const int MAX_N = 10000;

int N, M;
vector<int> G[MAX_N];
vector<int> rG[MAX_N];
vector<int> vs;
bool used[MAX_N];
int cmp[MAX_N];
int num[MAX_N];

void add_edge(int from, int to){
	G[from].push_back(to);
	rG[to].push_back(from);
}

void dfs(int v){
	used[v] = true;
	for(int i = 0; i < G[v].size(); i++){
		if(!used[G[v][i]]) dfs(G[v][i]);
	}
	vs.push_back(v);
}

void rdfs(int v, int k){
	used[v] = true;
	cmp[v] = k;
	for(int i = 0; i < rG[v].size(); i++){
		if(!used[rG[v][i]]) rdfs(rG[v][i], k);
	}
}

int scc(){
	fill(used, used + N, false);
	vs.clear();
	for(int i = 0; i < N; i++){
		if(!used[i]) dfs(i);
	}
	fill(used, used + N, false);
	int k = 0;
	for(int i = vs.size() - 1; i >= 0; i--){
		if(!used[vs[i]]) rdfs(vs[i], k++);
	}
	return k;
}

int main(){
	scanf("%d %d", &N, &M);
	for(int i = 0; i < M; i++){
		int u, v;
		scanf("%d %d", &u, &v);
		u--; v--;
		add_edge(u, v);
	}
	int n = scc();
	for(int i = 0; i < N; i++){
		num[cmp[i]]++;
	}
	int res = 0;
	for(int i = 0; i < n; i++){
		if(num[i] >= 2) res++;
	}
	printf("%d\n", res);
	return 0;
}

Peking University Online Judge 3250 Bad Hair Day

やるだけ(すみません調子こきました嘘です)
ループの中でやっていることは
・i番目の人に注目する
・もしスタックが空でない場合、仮にスタックの中にi番目の人より背が低い人がいたら、
その人からはi番目以降に来る人の頭は絶対に見えないので、スタックから出ていってもらう
・スタックの中の人は全員i番目の人の頭が見えるので、スタックの中の人数を答えにプラスする。
・スタックにi番目の人を入れる

#include<cstdio>
#include<stack>
using namespace std;
typedef long long int ll;

int N, h;
stack<int> sa;

int main(){
	scanf("%d", &N);
	ll res = 0LL;
	for(int i = 0; i < N; i++){
		scanf("%d", &h);
		while(!sa.empty() && sa.top() <= h) sa.pop();
		res += sa.size();
		sa.push(h);
	}
	printf("%lld\n", res);
	return 0;
}

Peking University Online Judge 2409 Let It Bead

昨日のコードを少しだけ弄ってAC

#include<cstdio>
#include<vector>
using namespace std;
typedef long long int ll;
static const int MAX_CS = 32;

int c, s;
vector<ll> va[MAX_CS + 1];

int gcd(int a, int b){
	if(b == 0) return a;
	else return gcd(b, a % b);
}

int main(){
	for(int i = 1; i <= MAX_CS; i++){
		for(int j = 0; j * i <= MAX_CS; j++){
			if(j == 0) va[i].push_back(1LL);
			else va[i].push_back(va[i].back() * (ll)i);
		}
	}
	for(;;){
		scanf("%d %d", &c, &s);
		if(c == 0 && s == 0) break;
		ll res = 0;
		for(int i = 1; i <= s; i++){
			if(i == 1 || i == s){
				res += va[c][s / i];
			}else if(gcd(s, i) != 1){
				res += va[c][gcd(s, i)];
			}else{
				res += c;
			}
		}
		for(int i = 1; i <= s; i++){
			if(s % 2 == 0){
				if(i % 2 == 0){
					res += va[c][s / 2];
				}else{
					res += va[c][(s - 2) / 2 + 2];
				}
			}else{
				res += va[c][(s - 1) / 2 + 1];
			}
		}
		printf("%lld\n", res / (ll)(s * 2));
	}
	return 0;
}

Peking University Online Judge 1286 Necklace of Beads

テストケースに0を忍ばせるとかいうアレ
ポリアの数え上げ定理を使いましたが、反転のパートは想像で書きました(バカなの?)

#include<cstdio>
#include<vector>
using namespace std;
typedef long long int ll;

int n;
vector<ll> va;

int gcd(int a, int b){
	if(b == 0) return a;
	else return gcd(b, a % b);
}

int main(){
	for(int i = 0; i <= 24; i++){
		if(i == 0) va.push_back(1LL);
		else va.push_back(va.back() * 3LL);
	}
	for(;;){
		scanf("%d", &n);
		if(n == -1) break;
		if(n == 0){
			printf("0\n");
			continue;
		}
		ll res = 0;
		for(int i = 1; i <= n; i++){
			if(i == 1 || i == n){
				res += va[n / i];
			}else if(gcd(n, i) != 1){
				res += va[gcd(n, i)];
			}else{
				res += 3;
			}
		}
		for(int i = 1; i <= n; i++){
			if(n % 2 == 0){
				if(i % 2 == 0){
					res += va[n / 2];
				}else{
					res += va[(n - 2) / 2 + 2];
				}
			}else{
				res += va[(n - 1) / 2 + 1];
			}
		}
		printf("%lld\n", res / (ll)(n * 2));
	}
	return 0;
}

Peking University Online Judge 2407 Relatives

オイラーのΦ関数を返すだけ
なぜこの実装でΦ関数が実現できるかを理解することが重要ではないかと(お前誰だよ)

#include<cstdio>
using namespace std;
typedef long long int ll;

ll euler_phi(ll n){
	ll res = n;
	for(ll i = 2; i * i <= n; i++){
		if(n % i == 0){
			res = res / i * (i - 1);
			for(; n % i == 0; n /= i);
		}
	}
	if(n != 1) res = res / n * (n - 1);
	return res;
}

int main(){
	ll n;
	for(;;){
		scanf("%lld", &n);
		if(n == 0) break;
		printf("%lld\n", euler_phi(n));
	}
	return 0;
}

Aizu Online Judge 1132 Circle and Points

POJ にsubmitしてTLEしたコードを、そのままAOJに投げつけたら通りました(は?)
2点を全通り選んで、その2点を通る半径1の円を描き、それで囲める点の個数の最大値を調べる、という方針で(嘘解法かも知れない)

#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
static const int MAX_N = 300;
 
#define EPS (1e-10)
 
class Point{
    public:
        double x, y;
 
        Point(double x = 0, double y = 0): x(x), y(y) {}
 
        Point operator + (Point p){ return Point(x + p.x, y + p.y);}
        Point operator - (Point p){ return Point(x - p.x, y - p.y);}
        Point operator * (double a){ return Point(a * x, a * y);}
        Point operator / (double a){ return Point(x / a, y / a);}
 
        double abs(){ return sqrt(norm());}
        double norm(){ return x * x + y * y;}
 
        bool operator < (const Point &p) const{
            return x != p.x ? x < p.x : y < p.y;
        }
 
        bool operator == (const Point &p) const{
            return fabs(x - p.x) < EPS && fabs(y - p.y) < EPS;
        }
};
 
class Circle{
    public:
        Point c;
        double r;
        Circle(Point c = Point(), double r = 0.0): c(c), r(r) {}
};
 
typedef Point Vector;
 
int N;
Point p[MAX_N];
 
double arg(Vector p){return atan2(p.y, p.x);}
Vector polar(double a, double r){return Point(cos(r) * a, sin(r) * a);}
 
double getDistance(Point a, Point b){
    return (a - b).abs();
}
 
pair<Point, Point> getCrossPoints(Circle c1, Circle c2){
    double d = (c1.c - c2.c).abs();
    double a = acos((c1.r * c1.r + d * d - c2.r * c2.r) / (2 * c1.r * d));
    double t = arg(c2.c - c1.c);
    return make_pair(c1.c + polar(c1.r, t + a), c1.c + polar(c1.r, t - a));
}
 
bool comp(const Point &a, const Point &b){
    return a.x < b.x;
}
 
int main(){
    for(;;){
        scanf("%d", &N);
        if(N == 0) break;
        for(int i = 0; i < N; i++){
            scanf("%lf %lf", &p[i].x, &p[i].y);
        }
        sort(p, p + N, comp);
        int res = 1;
        for(int i = 0; i < N; i++){
            for(int j = i + 1; j < N; j++){
                if(p[j].x - p[i].x > 2.0) break;
                else if(getDistance(p[i], p[j]) > 2.0) continue;
                pair<Point, Point> pp = getCrossPoints((Circle){p[i], 1.0}, (Circle){p[j], 1.0});
                Point p1 = pp.first, p2 = pp.second;
                int cnt1 = 2, cnt2 = 2;
                for(int k = 0; k < N; k++){
                    if(k == i || k == j) continue;
                    if(p[k].x - p[j].x > 1.0) break;
                    if(getDistance(p1, p[k]) < 1.0) cnt1++;
                    if(getDistance(p2, p[k]) < 1.0) cnt2++;
                }
                res = max(res, max(cnt1, cnt2));
            }
        }
        printf("%d\n", res);
    }
    return 0;
}