1. 程式人生 > >【USACO】2018 February Contest, Platinum題解

【USACO】2018 February Contest, Platinum題解

【比賽經歷】

  • 看完T1先寫了一個\(O(NM)\)的暴力,交一發,得分5/10,說明正確地理解了題意。
  • 感覺T1碼量挺大的,於是先放了一下。
  • T2是傻題,看完10min寫掉了,得分10/10。
  • 回過頭來把T1的線段樹碼了,一遍寫對,不用痛苦地調這個鬼題,提交,得分10/10,跑了1.8s,果然STL不能亂用,差點T了。
  • 時間一共過了1.5h-。
  • 再看T3,先寫了個暴力,找了找規律(還試了試OEIS),然後發現並證明了規律,過掉了。
  • 時間一共過了2h+。

【T1】Slingshot

【題目連結】

【題解連結】

【思路要點】

  • 對於每個詢問,我們本質上是要求\(Min_{i=1}^{N}\{t_i+|x_i-a|+|y_i-b|\}\)。
  • 把\((a,b)\),\((x,y)\)看做平面上的點,對於每個詢問\((a,b)\),我們需要找到其右上、右下、左上、左下四個矩形內某一個關於\(x\),\(y\),\(t\)的式子的最小值。
  • 把詢問按橫座標排序,對縱座標建立線段樹,然後就做完了。
  • 總時間複雜度\(O((N+M)log(N+M))\)。

【程式碼】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 200005;
const int MAXV = 1e9;
const long long INF = 1e18;
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
struct Heap {
	priority_queue <long long, vector<long long>, greater<long long> > Val, Del;
	void init() {
		while (!Val.empty()) Val.pop();
		while (!Del.empty()) Del.pop();
	}
	void ins(long long x) {Val.push(x); }
	void del(long long x) {Del.push(x); }
	long long query() {
		while (!Val.empty() && !Del.empty() && Val.top() == Del.top()) {
			Val.pop();
			Del.pop();
		}
		if (Val.empty()) return INF;
		else return Val.top();
	}
};
struct SegmentTree {
	struct Node {
		int lc, rc;
		long long Minb, Minc;
	} a[MAXN * 2];
	Heap b[MAXN], c[MAXN];
	int root, size, n;
	void update(int root) {
		int lc = a[root].lc, rc = a[root].rc;
		a[root].Minb = min(a[lc].Minb, a[rc].Minb);
		a[root].Minc = min(a[lc].Minc, a[rc].Minc);
	}
	void build(int &root, int l, int r) {
		root = ++size;
		if (l == r) {
			a[root].Minb = a[root].Minc = INF;
			return;
		}
		int mid = (l + r) / 2;
		build(a[root].lc, l, mid);
		build(a[root].rc, mid + 1, r);
		update(root);
	}
	void init(int x) {
		n = x;
		for (int i = 1; i <= n; i++)
			b[i].init(), c[i].init();
		size = root = 0;
		build(root, 1, n);
	}
	void add(int root, int l, int r, int pos, long long vbl, long long vcl) {
		if (l == r) {
			b[l].ins(vbl);
			c[l].ins(vcl);
			a[root].Minb = b[l].query();
			a[root].Minc = c[l].query();
			return;
		}
		int mid = (l + r) / 2;
		if (mid >= pos) add(a[root].lc, l, mid, pos, vbl, vcl);
		else add(a[root].rc, mid + 1, r, pos, vbl, vcl);
		update(root);
	}
	void add(int pos, long long vbl, long long vcl) {
		add(root, 1, n, pos, vbl, vcl);
	}
	void del(int root, int l, int r, int pos, long long vbl, long long vcl) {
		if (l == r) {
			b[l].del(vbl);
			c[l].del(vcl);
			a[root].Minb = b[l].query();
			a[root].Minc = c[l].query();
			return;
		}
		int mid = (l + r) / 2;
		if (mid >= pos) del(a[root].lc, l, mid, pos, vbl, vcl);
		else del(a[root].rc, mid + 1, r, pos, vbl, vcl);
		update(root);
	}
	void del(int pos, long long vbl, long long vcl) {
		del(root, 1, n, pos, vbl, vcl);
	}
	long long queryb(int root, int l, int r, int ql, int qr) {
		if (l == ql && r == qr) return a[root].Minb;
		long long ans = INF;
		int mid = (l + r) / 2;
		if (mid >= ql) ans = min(ans, queryb(a[root].lc, l, mid, ql, min(qr, mid)));
		if (mid + 1 <= qr) ans = min(ans, queryb(a[root].rc, mid + 1, r, max(mid + 1, ql), qr));
		return ans;
	}
	long long queryb(int l, int r) {
		return queryb(root, 1, n, l, r);
	}
	long long queryc(int root, int l, int r, int ql, int qr) {
		if (l == ql && r == qr) return a[root].Minc;
		long long ans = INF;
		int mid = (l + r) / 2;
		if (mid >= ql) ans = min(ans, queryc(a[root].lc, l, mid, ql, min(qr, mid)));
		if (mid + 1 <= qr) ans = min(ans, queryc(a[root].rc, mid + 1, r, max(mid + 1, ql), qr));
		return ans;
	}
	long long queryc(int l, int r) {
		return queryc(root, 1, n, l, r);
	}
} S, T;
struct info {int opt; long long x, y, val; };
bool operator < (info a, info b) {return a.x < b.x; }
int n, m;
info a[MAXN];
set <int> tmp;
map <int, int> res;
long long ans[MAXN];
int main() {
	freopen("slingshot.in", "r", stdin);
	freopen("slingshot.out", "w", stdout);
	read(n), read(m);
	for (int i = 1; i <= n; i++)
		read(a[i].x), read(a[i].y), read(a[i].val), a[i].opt = 1;
	for (int i = n + 1; i <= n + m; i++)
		read(a[i].x), read(a[i].y), a[i].val = i - n, a[i].opt = 2;
	sort(a + 1, a + n + m + 1);
	for (int i = 1; i <= n + m; i++)
		tmp.insert(a[i].y);
	int cnt = 1;
	for (set <int> :: iterator i = tmp.begin(); i != tmp.end(); i++, cnt++)
		res[*i] = cnt;
	S.init(cnt), T.init(cnt);
	for (int i = 1; i <= n + m; i++)
		if (a[i].opt == 1) T.add(res[a[i].y], a[i].x - a[i].y + a[i].val, a[i].x + a[i].y + a[i].val);
	for (int i = 1; i <= n + m; i++) {
		if (a[i].opt == 1) {
			S.add(res[a[i].y],-a[i].x - a[i].y + a[i].val,-a[i].x + a[i].y + a[i].val);
			T.del(res[a[i].y], a[i].x - a[i].y + a[i].val, a[i].x + a[i].y + a[i].val);
		} else {
			long long tans = abs(a[i].x - a[i].y);
			int pos = res[a[i].y];
			tans = min(tans, S.queryb(1, pos) + a[i].x + a[i].y);
			tans = min(tans, S.queryc(pos, cnt) + a[i].x - a[i].y);
			tans = min(tans, T.queryb(1, pos) - a[i].x + a[i].y);
			tans = min(tans, T.queryc(pos, cnt) - a[i].x - a[i].y);
			ans[a[i].val] = tans;
		}
	}
	for (int i = 1; i <= m; i++)
		writeln(ans[i]);
	return 0;
}

【T2】New Barns

【題目連結】

【題解連結】

【思路要點】

  • 維護森林中每一棵樹的任意一條直徑,在樹上距離任意一個點最遠的點中至少有一個是任意一條直徑的端點。
  • 在直徑為\((x,y)\)的樹上加入一個葉子結點\(z\),新樹的直徑至少有一條是\((x,y)\)、\((x,z)\)、\((y,z)\)中的一條。
  • 問題轉化為詢問森林中兩個點的距離,倍增即可。
  • 時間複雜度\(O(QLogQ)\)。

【程式碼】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
const int MAXLOG = 20;
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
void getopt(char &x) {
	x = getchar();
	while (x != 'B' && x != 'Q')
		x = getchar();
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int n, father[MAXN][MAXLOG];
int root[MAXN], depth[MAXN], x[MAXN], y[MAXN], d[MAXN];
int lca(int x, int y) {
	if (depth[x] < depth[y]) swap(x, y);
	for (int i = MAXLOG - 1; i >= 0; i--)
		if (depth[father[x][i]] >= depth[y]) x = father[x][i];
	if (x == y) return x;
	for (int i = MAXLOG - 1; i >= 0; i--)
		if (father[x][i] != father[y][i]) {
			x = father[x][i];
			y = father[y][i];
		}
	return father[x][0];
}
int dist(int x, int y) {
	return depth[x] + depth[y] - 2 * depth[lca(x, y)];
}
void update(int root, int pos) {
	int sx = dist(pos, x[root]);
	int sy = dist(pos, y[root]);
	int tmp, tx, ty;
	if (sx > sy) tmp = sx, tx = x[root], ty = pos;
	else tmp = sy, tx = y[root], ty = pos;
	if (tmp > d[root]) {
		d[root] = tmp;
		x[root] = tx;
		y[root] = ty;
	}
}
int main() {
	freopen("newbarn.in", "r", stdin);
	freopen("newbarn.out", "w", stdout);
	int q; read(q);
	while (q--) {
		char opt; int pos;
		getopt(opt), read(pos);
		if (opt == 'B') {
			n++;
			if (pos == -1) root[n] = x[n] = y[n] = n, depth[n] = 1, d[n] = 0;
			else {
				father[n][0] = pos;
				for (int i = 1; i < MAXLOG; i++)
					father[n][i] = father[father[n][i - 1]][i - 1];
				depth[n] = depth[pos] + 1;
				root[n] = root[pos];
				update(root[n], n);
			}
		} else writeln(max(dist(pos, x[root[pos]]), dist(pos, y[root[pos]])));
	}
	return 0;
}

【T3】Cow Gymnasts

【題目連結】

【題解連結】

【思路要點】

  • 首先,寫一個\(O(N^{N+2})\)的暴力,把可行的方案輸出,檢視規律。
  • 我們發現,所用的數極差不會超過1,並且,這一點是容易證明的。
  • 那麼,除去所有數都一樣的情況,考慮我們用了\(i\)和\(i+1\)。
  • 假設我們在\(j\)處放了一個\(i+1\),那麼也必須在\(j+i\)、\(j+2i\)、\(j+3i\)……處都放上\(i+1\),注意這裡的座標是在模意義下的。
  • 所以\(i\)對答案的貢獻應當是\(2^{gcd(i,N)}-2\)(要除去所有數都一樣的情況)。
  • 答案應當為\(N+\sum_{i=1}^{N-1}(2^{gcd(i,N)}-2)\),列舉gcd,用尤拉函式優化這個式子即可。
  • 時間複雜度\(O(N^{\frac{3}{4}}+\sqrt{N}*LogN)\),並且有著極小的常數(或者是更優的複雜度)。

【程式碼】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 5005;
const int P = 1e9 + 7;
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
long long n, ans;
long long power(long long x, long long y) {
	if (y == 0) return 1;
	long long tmp = power(x, y / 2);
	if (y % 2 == 0) return tmp * tmp % P;
	else return tmp * tmp % P * x % P;
}
long long gcd(long long x, long long y) {
	if (y == 0) return x;
	else return gcd(y, x % y);
}
int main() {
	freopen("gymnasts.in", "r", stdin);
	freopen("gymnasts.out", "w", stdout);
	read(n); ans = 1;
	for (int i = 1; i <= n - 1; i++) {
		ans += power(2, gcd(i, n)) - 1;
		ans %= P;
	}
	writeln(ans);
	return 0;
}