POJいろいろ8/3

3109
http://poj.org/problem?id=3109

座標圧縮+平面走査segtree。実装ややこしいはずだけど一発で通って気持ちがいい。実装ちゃんと練ればすんなりいくんだね。

3735
http://poj.org/problem?id=3735

1+B+B^2...の形が出てくるので行列の累乗和を貼って終了、と思ったらTLE。適当に定数倍であがいても通らない。
よくよく考えてみると定数項として一列行列を増やすと累乗和なんてやらずにいける。それで通るらしい。

3233
http://poj.org/problem?id=3233

3735で使った累乗和そのもの。

2674
http://poj.org/problem?id=2674

衝突があってもx座標の大きい順は変わらないことに注目する。
変な出力でWAだしてふざけんなよって思ったけど、コードも間違っていた…


2886
http://poj.org/problem?id=2886

BITの機能を拡張して累積和Sについてのlower_boundをできるようにする。BITの理解が深まった。
正負をうまく統合してやろうと思ったけど結局うまくいかず時間がかかった。普通に場合分けした方が早かった。

下は2886のコード

struct BIT { //1-origin!!!
	int n, b; vi bit;
	void init(int mx) {
		n = mx;
		b = 1;
		while(b * 2 <= n) b *= 2;
		bit = vi(n + 1, 0);
	}
	BIT(int mx = 0) { init(mx); } 

	ll get(int i) {
		ll s = 0;
		while(i > 0) { s += bit[i]; i -= i & -i; }
		return s;
	}
	void update(int i, ll x) {
		while(i <= n) { bit[i] += x; i += i & -i; }
	}

	int get_index(ll x) { //something like lower_bound(all(sum), a). O(logn)
		x--;
		int r = b;
		int res = 0;
		while(r > 0) {
			if((r | res) <= n) {
				int rv = bit[r | res];
				if(x >= rv) {
					x -= rv;
					res += r;
				}
			}
			r >>= 1;
		}
		return res + 1;
	}

	int range_sum(int a, int b) { //[a, b]
		return get(b) - get(a - 1);
	}
};

int N, S;
BIT bit;
char name[MAX_N][11];
int A[MAX_N];
int G[MAX_N];
int T[MAX_N];

int main() { //1-origin
	scanf("%d%d", &N, &S);
	rep(i, 1, N + 1) scanf("%s%d", name[i], &A[i]);
	bit.init(N);
	rep(i, 1, N + 1) bit.update(i, 1);

	int at = S;
	G[1] = at;
	rep(i, 1, N) {
		bit.update(at, -1);
		int a = A[at];
		if(a >= 0) {
			a %= (N - i);
		}
		else {
			a *= -1;
			a %= (N - i);
			a = N - i + 1 - a;
		}
		if(a == 0) a = N - i;
		if(a > N - i) a = 1;
		int tsum = bit.range_sum(at + 1, N);
		if(tsum >= a) {
			at = bit.get_index(N - i - tsum + a);
		}
		else {
			a -= tsum;
			at = bit.get_index(a);
		}
		G[i + 1] = at;
	}

	for(int i = 1; i <= N; i++) {
		for(int j = i; j <= N; j += i) {
			T[j]++;
		}
	}

	int res = -inf, rat = -1;
	rep(i, 1, N + 1) {
		if(res < T[i]) {
			res = T[i]; rat = i;
		}
	}
	printf("%s %d\n", name[G[rat]], T[rat]);

	return 0;

POJいろいろ8/2

2441
http://poj.org/problem?id=2441

bitdpやるだけしたら3938MS/4000MSで超ギリギリだったw

3254
http://poj.org/problem?id=3254

sampleも一発で通って気持ちがいい。bitdp。

3420
http://poj.org/problem?id=3420

行列累乗。一行ごとのstateをbitで表して行列を作る。16*16なので埋め込んでAC。ライブラリ化もしといた。

3171
http://poj.org/problem?id=3171

segtreeでdp高速化。[a, b)の区間を考えてやったのだから、当然updateすべきはb-1なのに、区間に含まれないbでupdateしていたのは良くない。

1274
http://poj.org/problem?id=1274

flowやるだけ。

2836
http://poj.org/problem?id=2836

前処理してbitdp。これも意外に思いつくのに時間がかかった。累積和も思いつかなかったし、前処理苦手?
何回も計算しているところ、無駄なところに注目すると前処理思いつきやすい?

POJいろいろ8/1

3185
http://poj.org/problem?id=3185

普通に端から反転させるだけ…と思ったが、端の場合分けで反転の回数カウントしていなくてWA生やしまくった。

1222
http://poj.org/problem?id=1222

これも端を決めて反転するだけ。反転は決めることが本質。

2100
http://poj.org/problem?id=2100

さすがにしゃくとりやるだけ。Two Pointersってしゃくとりのことだったのか…

3977
http://poj.org/problem?id=3977

半分全列挙。コードが煩雑になりがち。long longにabsが使えなくてCE。scanf("%d",a)としてWA。cin使いたい。

2549
http://poj.org/problem?id=2549

最大を求めよって文を見逃す。map>を使ったら、>>を> >にしろや、TLEするやで散々。
あと半分全列挙って言われなかったら解法思いつかなかったかも。

2566
http://poj.org/problem?id=2566

累積和をとるという発想が出てこなかった。区間和は累積和。NをN+1にしたらWAになったし尺取りは範囲が難しい。

2566のコード

int N;
int Q;
int A[MAX_N];
pl S[MAX_N];

ll _abs(ll a) {
	return a > 0 ? a : -a;
}

pair<ll, pi> solve2(ll m) {
	int r = -1;
	ll res = linf; pi at;
	rep(i, 0, N) {
		while(S[r + 1].fst - S[i].fst <= m && r != N - 1) {
			r++;
		}
		if(_abs(res - m) > _abs(S[r].fst - S[i].fst - m) && r != i) {
			res = S[r].fst - S[i].fst;
			at = pi(S[r].sec, S[i].sec);
		}
		if(_abs(res - m) > _abs(S[r + 1].fst - S[i].fst - m)) {
			res = S[r + 1].fst - S[i].fst;
			at = pi(S[r + 1].sec, S[i].sec);
		}
	}
	if(at.fst > at.sec) swap(at.fst, at.sec);
	return make_pair(res, at);
}

void solve(int M) {
	S[0] = pi(0, 0);
	rep(i, 0, N) {
		S[i + 1].fst = S[i].fst + A[i];
		S[i + 1].sec = i + 1;
	}
	sort(S, S + N + 1);
	pair<ll, pi> r1 = solve2(M);
	printf("%lld %d %d\n", r1.fst, r1.sec.fst + 1, r1.sec.sec);
}


int main() {
	while(true) {
		scanf("%d%lld", &N, &Q);
		if(N == 0 && Q == 0) break;
		rep(i, 0, N) scanf("%d", &A[i]);
		while(Q--) {
			int a; scanf("%d", &a);
			solve(a);
		}
	}
	return 0;
}

POJ 3045: Cow Acrobats

http://poj.org/problem?id=3045

上からi-1番目まで決めているとする。i-1番目までの重さをWとすれば
i番目とi+1番目のriskは
W-S[i]
W+A[i]-S[i+1](Aは重さ)となる。ここでi番目とi+1番目のcowを入れ替えてみる

W-S[i+1]
W+A[i+1]-S[i]となる。
W-S[i]<=W+A[i+1]-S[i],W-S[i+1]<=W+A[i]-S[i+1]なので、結局
W+A[i]-S[i+1]とW+A[i+1]-S[i]の大小が重要であり、前者が小さくなるためには
A[i]+S[i]<=A[i+1]+S[i+1]であればよい。
よってA[i]+S[i]でsortして順番に使えばよい。

int A[MAX_N], B[MAX_N];
int C[MAX_N];

int N;

bool compare(int a, int b) {
	return A[a] + B[a] <= A[b] + B[b];
}


int main() {
	scanf("%d", &N);
	rep(i, 0, N) {
		scanf("%d%d", A + i, B + i);
		C[i] = i;
	}
	sort(C, C + N, compare);
	ll m = -(1LL << 60), sum = 0;
	rep(i, 0, N) {
		MAX(m, sum - B[C[i]]);
		sum += A[C[i]];
	}
	
	printf("%lld\n", m);
	return 0;
}

二分探索のsectionにある練習問題だけど二分探索出てこないぞ…
このA[i]+S[i]の条件を出すのに少しこんがらがった。xy平面使ったらわかりやすかったかも?

AOJ 0531: Paint Color

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0531


座標圧縮+二次元imos。

久しぶりに二次元imosしたけどこっちはうまくいった。座標圧縮で少しこんがらがった。
長方形の辺が存在しない区間の長さを1にする感じ。

int fd(const vector<int>& vec, int a) {
	return lower_bound(all(vec), a) - vec.begin();
}
int dx[4] = {1, 0, -1, 0};
int dy[4] = {0, 1, 0, -1};

int H, W;
int N;
int C[MAX_N][MAX_N];
int X1[MAX_N], Y1[MAX_N], X2[MAX_N], Y2[MAX_N];

void solve() {
	while(true) {
		scanf("%d%d", &H, &W);
		if(H == 0 && W == 0) break;
		scanf("%d", &N);
		memset(C, 0, sizeof(C));
		vector<int> X, Y;
		X.push_back(0); X.push_back(H - 1);
		Y.push_back(0); Y.push_back(W - 1);
		rep(i, 0, N) {
			scanf("%d%d%d%d", &X1[i], &Y1[i], &X2[i], &Y2[i]);
			X2[i]--; Y2[i]--;
			X.push_back(X1[i]); X.push_back(X2[i]);
			Y.push_back(Y1[i]); Y.push_back(Y2[i]);
		}
		int nx = (int)X.size(), ny = (int)Y.size();
		rep(i, 0, nx - 1) if(X[i + 1] - X[i] != 1 && X[i] != H - 1) X.push_back((X[i] + 1));
		rep(i, 0, ny - 1) if(Y[i + 1] - Y[i] != 1 && Y[i] != W - 1) Y.push_back((Y[i] + 1));
		sort(all(X));
		sort(all(Y));
		X.erase(unique(all(X)), X.end());
		Y.erase(unique(all(Y)), Y.end());
		nx = X.size(); ny = Y.size();

		rep(i, 0, N) {
			int nx1 = fd(X, X1[i]); nx1++;
			int nx2 = fd(X, X2[i]); nx2++;
			int ny1 = fd(Y, Y1[i]); ny1++;
			int ny2 = fd(Y, Y2[i]); ny2++;
			C[nx2 + 1][ny2 + 1]++;
			C[nx1][ny2 + 1]--;
			C[nx2 + 1][ny1]--;
			C[nx1][ny1]++;
		}

		rep(i, 1, nx + 1) {
			rep(j, 1, ny + 1) {
				C[i][j] += C[i][j - 1] + C[i - 1][j] - C[i - 1][j - 1];
			}
		}
		
		queue<pi> que;
		int res = 0;
		rep(i, 1, nx + 1) {
			rep(j, 1, ny + 1) {
				if(C[i][j]) continue;
				que.push(pi(i, j));
				while(!que.empty()) {
					pi p = que.front(); que.pop();
					rep(i, 0, 4) {
						if(1 <= p.fst + dx[i] && p.fst + dx[i] <= nx
								&& 1 <= p.sec + dy[i] && p.sec + dy[i] <= ny) {
							if(!C[p.fst + dx[i]][p.sec + dy[i]]) {
								C[p.fst + dx[i]][p.sec + dy[i]] = 1;
								que.push(pi(p.fst + dx[i], p.sec + dy[i]));
							}
						}
					}
				}
				res++;
			}
		}
		cout << res << "\n";
	}
}

POJのせいでscanfとcoutが混ざってて草。

POJ2010: Moo University - Financial Aid

http://poj.org/problem?id=2010

蟻本でpriority_queueと二分探索のどちらでも取り上げられているように解法が2通りある。

1.
http://omochan.hatenablog.com/entry/2017/06/30/214614
この問題と同じように、真ん中を決めてしまえば独立に考えられるので前後で前処理を施す。
その際priority_queueを使えばO(NlogN)でできる。

2.
真ん中を二分探索する。適当にやってもO(N(logN)^2)で通る。

下のcodeは解法1のもの。

int N, C, F;
pi P[100010];
ll S[100010], rS[100010];

void get_sum(ll *s) {
	ll sum = 0;
	priority_queue<int> que;
	int n = (N - 1) / 2;
	for(int i = 0; i < C; i++) {
		if(que.size() < n) {
			sum += P[i].second;
			que.push(P[i].second);
		}
		else {
			if(que.top() > P[i].second) {
				sum -= que.top(); que.pop();
				sum += P[i].second; que.push(P[i].second);
			}
		}
		if(que.size() == n) s[i] = sum;
		else s[i] = INF;
	}
}

int main() {
	scanf("%d%d%d", &N, &C, &F);
	for(int i = 0; i < C; i++) {
		int a, b;
		scanf("%d%d", &a, &b);
		P[i] = make_pair(a, b);
	}
	if(N == 1) {
		int ans = -1;
		for(int i = 0; i < C; i++) {
			if(P[i].second > F) continue;
			ans = max(ans, P[i].first);
		}
		printf("%d\n", ans);
	}
	else {
		sort(P, P + C);
		get_sum(S);
		reverse(P, P + C);
		get_sum(rS);
		reverse(rS, rS + C);
		reverse(P, P + C);
		int ans = -1;
		for(int i = 1; i < C - 1; i++) {
			if(S[i - 1] + rS[i + 1] + P[i].second <= F) ans = max(ans, P[i].first);
		}
		printf("%d\n", ans);
	}
}

POJ 3104: Drying他

蟻本少しずつ埋めます。

http://poj.org/problem?id=3104

POJ久しぶりにしたので注意点を。(POJ以外の注意点もあり)

  • cout,cinはios::sync_with_stdio(false)にしても遅いのでprintf,scanfを使う。
  • c++11は使えないのでtemplateらへんでcompile errorが生えるのを避ける。
  • accumulateをdoubleで扱いたいなら第3引数を0.0にする。

ACされたコードを丸々載せておく。

#include <iostream>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <string>
#include <vector>
#include <queue>
#include <string.h>
#include <map>
#include <fstream>
#include <functional>
#include <bitset>
#include <stack>
#include <set>
#include <climits>
#include <numeric>
#include <complex>
#define ADD(a, b) a = (a + (ll)b) % mod
#define MUL(a, b) a = (a * (ll)b) % mod
#define MAX(a, b) a = max(a, b)
#define MIN(a, b) a = min(a, b)
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define rer(i, a, b) for(int i = (a) - 1; i >= (b); i--)
#define all(a) (a).begin(), (a).end()
#define sz(v) (int)(v).size()
#define pb push_back
#define sec second
#define fst first
#define debug(fmt, ...) Debug(__LINE__, ":", fmt, ##__VA_ARGS__)
using namespace std;
typedef long long ll;
typedef pair<int, int> pi;
typedef pair<ll, ll> pl;
typedef pair<int, pi> ppi;
typedef vector<ll> vi;
typedef complex<double> comp;
void Debug() {cout << '\n'; }
template<class FIRST, class... REST>void Debug(FIRST arg, REST... rest) { 
	cout << arg << " "; Debug(rest...); }
template<class T>ostream& operator<< (ostream& out, const vector<T>& v) {
	out << "[";if(!v.empty()){rep(i,0,sz(v)-1)out<<v[i]<< ", ";out<<v.back();}out << "]";return out;}
template<class S, class T>ostream& operator<< (ostream& out, const pair<S, T>& v) {
	out << "(" << v.first << ", " << v.second << ")";return out;}
const int MAX_N = 200010;
const double eps = 1e-6;
const ll mod = 1000000007;
const int inf = 1 << 30;
const ll linf = 1LL << 60;
const double PI = 3.14159265358979323846;
///////////////////////////////////////////////////////////////////////////////////////////////////

int N, C;
ll A[MAX_N];

bool ok(ll m) {
	ll res = 0;
	rep(i, 0, N) {
		if(A[i] - m > 0) res += (A[i] - m + C - 1) / C;
	}
	if(res <= m) return true;
	else return false;
}

void solve() {
	scanf("%d", &N);
	rep(i, 0, N) scanf("%d", &A[i]);
	cin >> C; C--;
	if(C == 0) {
		ll mx  = -1;
		rep(i, 0, N) MAX(mx, A[i]);
		printf("%lld\n", mx);
	}
	else {
		ll lb = -1, ub = (1LL << 60);
		while(ub - lb > 1) {
			ll m = (ub + lb) / 2;
			if(ok(m)) ub = m;
			else lb = m;
		}
		printf("%lld\n", ub);
	}
}

int main() {
	solve();
	return 0;
}