【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
using namespace std;
const int MAXN = 200005;
const int MAXV = 1e9;
const long long INF = 1e18;
template  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  void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template  void writeln(T x) {
	write(x);
	puts("");
}
struct Heap {
	priority_queue , greater > 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  tmp;
map  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  :: 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
using namespace std;
const int MAXN = 100005;
const int MAXLOG = 20;
template  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  void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template  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
using namespace std;
const int MAXN = 5005;
const int P = 1e9 + 7;
template  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  void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template  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;
}


你可能感兴趣的:(【OJ】USACO,【数据结构】线段树,【算法】树的直径,【算法】欧拉函数,【算法】数学,【算法】找规律)