読者です 読者をやめる 読者になる 読者になる

Lilliput Steps

小さな一歩から着実に. 数学やプログラミングのことを書きます.

JOI 本選 2013 - 2014 オンライン参加記

目標 : 後輩ズに勝つ /\_/\

で参加する予定で, 2 まででいいだろ~と思っていたらハマって4 の考察までして, 試験前の貴重な4 時間を失いました(たのしかったです)


1. JOI 紋章

JOI 旗が取り上げられるのは今回で4 回目なはず. 間違いない.
straight-forward. $O(mn)$

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

int n, m;
char flag[1024][1024];
char emblem[2][3];

bool check(int i, int j)
{
	if (i < 0 || i + 1 >= m) return (false);
	if (j < 0 || j + 1 >= n) return (false);
	return (strncmp(flag[i] + j, emblem[0], 2) == 0 && strncmp(flag[i + 1] + j, emblem[1], 2) == 0);
}

int main()
{
	scanf("%d %d", &m, &n);
	
	for (int i = 0; i < m; i++){
		scanf("%s", flag[i]);
	}
	
	scanf("%s", emblem[0]); scanf("%s", emblem[1]);
	
	int ans = 0;
	for (int i = 0; i <= m - 2; i++){
		for (int j = 0; j <= n - 2; j++){
			if (check(i, j)){
				ans++;
			}
		}
	}
	
	int base = ans;
	char rot[] = "JOI";
	for (int i = 0; i < m; i++){
		for (int j = 0; j < n; j++){
			char bef = flag[i][j];
			int ct = 0;
			
			if (check(i - 1, j)) ct++;
			if (check(i - 1, j - 1)) ct++;
			if (check(i, j - 1)) ct++;
			if (check(i, j)) ct++;
			
			for (int k = 0; k < 3; k++){
				int tmp = base - ct;
				flag[i][j] = rot[k];
				
				if (check(i - 1, j)) tmp++;
				if (check(i - 1, j - 1)) tmp++;
				if (check(i, j - 1)) tmp++;
				if (check(i, j)) tmp++;
				
				flag[i][j] = bef;
				ans = max(ans, tmp);
			}
		}
	}
	
	printf("%d\n", ans);
}

2. JOI 饅頭

例年の2番DP 枠よりは易化してると思った.
ところでみなさん2*10^4 まで見ると言ってますがそれはいったい...

#include <cstdio>
#include <climits>
#include <algorithm>

using namespace std;


int main()
{
	int m, n;
	int a[10000];
	int sum[10001];
	
	scanf("%d %d", &m, &n);
	
	for (int i = 0; i < m; i++){
		scanf("%d", a + i);
	}
	
	sort(a, a + m);
	reverse(a, a + m);
	
	sum[0] = 0;
	
	for (int i = 1; i <= m; i++) sum[i] = sum[i - 1] + a[i - 1];
	
	int dp[100001];
	fill(dp, dp + 100000, INT_MAX / 2);
	
	dp[0] = 0;
	for (int i = 0; i < n; i++){
		int t, v;
		scanf("%d %d", &t, &v);
		for (int j = 10000; j - t >= 0; j--){
			dp[j] = min(dp[j], dp[j - t] + v);
		}
		for (int j = 10000; j >= 1; j--){
			dp[j - 1] = min(dp[j], dp[j - 1]);
		}
	}
	
	int ans = 0;
	for (int i = 0; i <= m; i++) ans = max(ans, sum[i] - dp[i]);
	printf("%d\n", ans);
	
	return (0);
}

3. バームクーヘン

春はねむぽよなので, 頭が眠っていて, ある地点X から和がS になる区間を高速にもとめるのむずいなあ, どうしようと悩んでいて, 僕なりに出た結論が巡回スケジューリングでした(絶望).
ソートがボトルネックでバケットソートする程度には時間を潰しましたね...w

#include <cstdio>
#include <algorithm>
#include <vector>

using namespace std;

typedef long long lint;

int main()
{
	int n;
	static int a[100000];
	static int s[200000], t[200000], next[2][200000];
	static pair<int, int> p[400000];
	static vector<int> bucket[300000];
	
	lint sum = 0;
	
	scanf("%d", &n);
	
	for (int i = 0; i < n; i++){
		scanf("%d", a + i);
		sum += a[i];
	}
	
	lint left = 0, right = sum / 3;
	
	while (left != right){
		lint mid = (left + right + 1) >> 1;
		lint sum2 = 0;
		int head = 0, tail = 0;
		bool ng = false;
		
		for (; head < n; head++){
			while (sum2 < mid){
				sum2 += a[tail++];
				if (tail >= n) tail -= n;
			}
			int i = head;
			s[i] = head, t[i] = tail;
			if (s[i] == t[i]) ng = true;
			if (t[i] < s[i]) t[i] += n;
			s[n + i] = s[i] + n, t[n + i] = t[i] + n;
			sum2 -= a[head];
		}
		if (ng){
			right = mid - 1;
			continue;
		}
		for (int i = 0; i < n * 3; i++) bucket[i].clear();
		
		for (int i = 0; i < n * 2; i++){
			bucket[t[i]].push_back(i);
			bucket[s[i]].push_back(n * 2 + i);
		}
		int ctr = 0;
		for (int i = 0; i < n * 3; i++){
			for (int j = 0; j < bucket[i].size(); j++){
				p[ctr++] = make_pair(i, bucket[i][j]);
			}
		}
		
		int last = -1;
		for (int i = n * 4 - 1; i >= 0; i--){
			int id = p[i].second;
			if (id < n * 2) next[0][id] = last;
			else {
				id -= n * 2;
				if (last < 0 || t[last] > t[id]) last = id;
			}
		}
		
		for (int k = 0; k + 1 < 2; k++){
			for (int i = 0; i < n * 2; i++){
				if (next[k][i] < 0) next[k + 1][i] = -1;
				else next[k + 1][i] = next[k][next[k][i]];
			}
		}
		
		int res = 0;
		for (int i = 0; i < n; i++){
			int tmp = 0, j = i;
			for (int k = 1; k >= 0; k--){
				int j2 = next[k][j];
				if (j2 >= 0 && t[j2] <= s[i] + n){
					j = j2;
					tmp |= 1 << k;
				}
			}
			if (res >= 3) break;
			res = max(res, tmp + 1);
		}
		if (res >= 3) left = mid;
		else right = mid - 1;
	}
	
	printf("%lld\n", left);
	
	return (0);
}

4. フクロモモンガ
25 点欲しかったけど貰えなかった. ちょっとこれはフィードバックをみないとデバッグできない.

#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>

using namespace std;

typedef long long lint;

vector<pair<int, int> > G[1000 * 101];
int h[1024];
int a, b, t;

int main()
{
	int n, m, x;
	
	scanf("%d %d %d", &n, &m, &x);
	
	if (n > 1024) return ((int)"あたしのいくじなし~><");
	
	for (int i = 0; i < n; i++){
		scanf("%d", h + i);
		for (int j = 0; j < h[i]; j++){
			G[i * 101 + j].push_back(make_pair(i * 101 + j + 1, 1));
			G[i * 101 + j + 1].push_back(make_pair(i * 101 + j, 1));
		}
	}
	
	for (int i = 0; i < m; i++){
		scanf("%d %d %d", &a, &b, &t);
		--a; --b;
		for (int j = t; j <= h[a] && j - t <= h[b]; j++){
			G[a * 101 + j].push_back(make_pair(b * 101 + j - t, t));
		}
		for (int j = t; j <= h[b] && j - t <= h[a]; j++){
			G[b * 101 + j].push_back(make_pair(a * 101 + j - t, t));
		}
	}
	
	priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > pq;
	
	
	bool done[1000 * 101] = {0};
	
	for (pq.push(make_pair(0, x)); pq.size(); pq.pop()){
		pair<int, int> tmp = pq.top();
		if (done[tmp.second]) continue;
		done[tmp.second] = true;
		if (tmp.second == (n - 1) * 101 + h[n - 1]) return (!printf("%d\n", tmp.first));
		
		for (int i = 0; i < G[tmp.second].size(); i++){
			pair<int, int> next = G[tmp.second][i];
			if (done[next.first]) continue;
			pq.push(make_pair(tmp.first + next.second, next.first));
		}
	}
	
	return (!printf("-1\n"));
}

5. 切り取り線
Ω\ζ°)チーン

6. まとめ
たのしかったよ~~