2018.08.23高二互测

2018.08.24 NOIp模拟赛

今天是doe的神题,表示根本做不出,还好可以(~IOI~)实时评测,不然第一题都被(~Subtask~)捆死了。。。

第一题

第一眼看着题感觉把所有的叶子节点都染黑很优秀,但看样例就知道是错的。于是发现对于一个点,在他的儿子节点中,只要选得只剩下一个就是正确并且优秀的。而当前点被选之后,就把他标记一下不计入他父亲节点那一层的答案统计。但是这个DP会根据树的形态而改变,比如说对于一条链,如果选取两端作为根节点则答案为(~0),但答案应为(~1),所以挑一个好根很重要。再发现,若根的度大于(~2)时,其连向祖先的联通块中必然有点为⿊点,所以就选一个度大于等于(~3)的就好了。

code

#include<bits/stdc++.h>
#define For(i, j, k) for(int i = j; i <= k; ++i)
#define Forr(i, j, k) for(int i = j; i >= k; --i)
#define Travel(i, u) for(int i = beg[u], v = to[i]; i; i = nex[i], v = to[i])
using namespace std;

inline int read() {
	int x = 0, p = 1; char c = getchar();
	for(; !isdigit(c); c = getchar()) if(c == '-') p = -1;
	for(; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
	return x *= p;
}

inline void File() {
	freopen("bf.in", "r", stdin);
	freopen("bf.out", "w", stdout);
}

const int N = 1e5 + 10;
int n, e = 1, beg[N], nex[N << 1], to[N << 1];
int siz[N], u, v, deg[N], rt, ans, dp[N], maxx = 0;

inline void add(int x, int y) {
	to[++ e] = y, nex[e] = beg[x], beg[x] = e, ++ deg[x];
	to[++ e] = x, nex[e] = beg[y], beg[y] = e, ++ deg[y];
}

inline void dfs(int u, int f) {
	siz[u] = 0;
	Travel(i, u) if (v != f) {
		dfs(v, u);
		int sz = 0, tot = 0;
		for (int t = beg[v]; t; t = nex[t]) if (to[t] != u) ++ tot, sz += siz[to[t]] == 0;
		ans += max(0, sz - 1);
		siz[v] = tot > 1 && tot == sz || tot != sz;
	}
}

int main() {
	File();
	n = read();
	For(i, 2, n) u = read(), v = read(), add(v, u);
	For(i, 0, n - 1) if (deg[i] > maxx) rt = i, maxx = deg[i]; 
	dfs(rt, -1);

	int sz = 0;
	Travel(i, rt) if (!siz[v]) ++ sz;
	ans += max(0, sz - 1);
	
	cout << ans << endl;
	return 0;
}

第二题

​ 这是一道很思维的题啊,离散化,首先对错位的数向应在的位置连一条边,可以发现最后的图是一些互不相交的简单环。当几个环中有相同的点权时,可以通过断开再连接的方式把这几个环合并成一个环以减少环数。对于若干个环,先把这几个环首尾连接变成一个大环进行操作,可以发现这样之后只有每个小环的末尾位置没有复位,再对这些数连成一个环。那么对于若干个环最少只需要两次操作就好了。但是有一个(~S~)的限制,由于我很懒就直接搬(~Solution~)了。

code

#include<bits/stdc++.h>
#define For(i, j, k) for(int i = j; i <= k; ++i)
#define Forr(i, j, k) for(int i = j; i >= k; --i)
#define Travel(i, u) for(int i = beg[u], v = to[i]; i; v = to[i = nex[i]])
using namespace std;

inline int read() {
	int x = 0, p = 1; char c = getchar();
	for(; !isdigit(c); c = getchar()) if(c == '-') p = -1;
	for(; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
	return x *= p;
}

inline void File() {
	freopen("gen.in", "r", stdin);
	freopen("gen.out", "w", stdout);
}

const int N = 2e5 + 10;
int n, s, fa[N], e = 1, beg[N], to[N << 1], nex[N << 1], num[N << 1];
int c[N], tot, a[N], ls[N], vis[N], cnt, k;
int lst[N], c1, c2;

vector <int> ans[N];

inline void add(int x, int y, int z) {
	to[++ e] = y, nex[e] = beg[x], beg[x] = e, num[e] = z;
}

inline void dfs(int u) {
	vis[u] = cnt;
	while (beg[u]) {
		int now = beg[u]; beg[u] = nex[beg[u]];
		dfs(to[now]), ans[cnt].push_back(num[now]);
	}
}

int main() {
	File();
	n = read(), s = read();
	For(i, 1, n) a[i] = ls[i] = read(), fa[i] = i;;
	sort(ls + 1, ls + 1 + n);
	For(i, 1, n) c[i] = ls[i] == ls[i - 1] ? c[i - 1] : c[i - 1] + 1;

	tot = unique(ls + 1, ls + 1 + n) - ls - 1;
	For(i, 1, n) {
		a[i] = lower_bound(ls + 1, ls + 1 + tot, a[i]) - ls;
		if (a[i] != c[i]) ++ k, add(a[i], c[i], i);		
	}

	if (k > s) return puts("-1"), 0;
	For(i, 1, n) if (!vis[i] && beg[i]) ++ cnt, dfs(i); 	

	if (cnt <= 1 || s - k <= 1) {
		printf("%d
", cnt);
		For(i, 1, cnt) {
			printf("%d
", ans[i].size());
			for (auto v : ans[i]) printf("%d ", v); puts("");
		}
		return 0;
	}

	cout << cnt - min(cnt, s - k) + 2 << endl;

	For(i, s - k + 1, cnt) {
		printf("%d
", ans[i].size());
		for (auto v : ans[i]) printf("%d ", v); puts("");
	}

	if (s - k > 0) {
		Forr(i, min(cnt, s - k), 1) {
			c1 += ans[i].size();
			lst[++ c2] = ans[i][0];
		}			
		
		printf("%d
", c1); 
		For(i, 1, min(cnt, s - k)) for (auto v : ans[i]) printf("%d ", v); puts("");
		printf("%d
", c2);
		For(i, 1, c2) printf("%d ", lst[i]); puts("");
	}
	return 0;
}

第三题

​ 给定⼀棵(~n~)个点树和树上的(~m~)条路径( 互不相同),求这(~m~)条路径中任选两条时其中⼀条完全包含另⼀
条的概率是多少。

​ 只要画个图就很容易推出满足题意的两种情况(一对子树限制和两对区间限制),就不再赘述。考虑用主席树维护(~dfn~),对于每一条路径,在主席树中插入其另一端点的(~dfn~),在查询的时候,由于一棵子树的(~dfn~)是连续的,所以只要查询以在当前这个区间中的(~dfn~)为目标区间的节点个数。但一条路径的端点可以互逆,所以记得反过来查询一下。

code

#include<bits/stdc++.h>
#define For(i, j, k) for(register int i = j; i <= k; ++i)
#define Forr(i, j, k) for(register int i = j; i >= k; --i)
#define Travel(i, u) for(register int i = beg[u], v = to[i]; i; i = nex[i], v = to[i])
using namespace std;

inline int read() {
	int x = 0, p = 1; char c = getchar();
	for(; !isdigit(c); c = getchar()) if(c == '-') p = -1;
	for(; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
	return x *= p;
}

inline void File() {
	freopen("std.in", "r", stdin);
	freopen("std.out", "w", stdout);
}

typedef long long ll;
const int N = 2e5 + 10, M = N << 1;
int e = 1, beg[N], nex[M], to[M];
int dep[N], tot, pos[N], fa[18][N];
int n, m, u, v, st[N], ed[N], rt[N];
struct node { int x, y; } P[N];
ll ansx, ansy;

inline void add(int x, int y) {
	to[++ e] = y, nex[e] = beg[x], beg[x] = e;
	to[++ e] = x, nex[e] = beg[y], beg[y] = e;
}

inline void dfs(int u, int f) {
	pos[st[u] = ++ tot] = u, dep[u] = dep[f] + 1;	
	fa[0][u] = f;
	Travel(i, u) if (v != f) dfs(v, u);
	ed[u] = tot;
}

namespace PRE {
#define mid (l + r >> 1)
	struct node { int l, r, v; } tr[N * 30];
	int cnt = 0;

	inline void update(int &now, int pre, int l, int r, int x) {
		now = ++ cnt, tr[now] = tr[pre], ++ tr[now].v;					
		if (l < r) {
			if (x <= mid) update(tr[now].l, tr[pre].l, l, mid, x);
			else update(tr[now].r, tr[pre].r, mid + 1, r, x);
		}
	}

	inline int query(int u, int v, int l, int r, int L, int R) {
		if (tr[v].v - tr[u].v == 0) return 0;
		if (L <= l && r <= R) return tr[v].v - tr[u].v;
		if (R <= mid) return query(tr[u].l, tr[v].l, l, mid, L, R);
		if (L > mid) return query(tr[u].r, tr[v].r, mid + 1, r, L, R);
		return query(tr[u].l, tr[v].l, l, mid, L, R) + query(tr[u].r, tr[v].r, mid + 1, r, L, R);
	}

#undef mid
};

vector <int> vec[N];

int main() {
	File(), read();
	using namespace PRE;

	n = read(), m = read();
	For(i, 2, n) u = read(), v = read(), add(u, v);
	dfs(1, 0);

	For(j, 1, 17) For(i, 1, n) fa[j][i] = fa[j - 1][fa[j - 1][i]];
	For(i, 1, m) P[i].x = read(), P[i].y = read(), vec[P[i].x].push_back(P[i].y);
	
	For(i, 1, n) { // this if dfn
		int v = pos[i];
		if (vec[v].size() == 0) rt[i] = rt[i - 1];
		else {
			update(rt[i], rt[i - 1], 1, n, st[vec[v][0]]);
			For(j, 1, vec[v].size() - 1) update(rt[i], rt[i], 1, n, st[vec[v][j]]);
		}
	}

	For(i, 1, m) {
		int x = P[i].x, y = P[i].y;
		if (dep[x] < dep[y]) swap(x, y);

		if (st[y] <= st[x] && st[x] <= ed[y]) {
			int t = x; Forr(i, 17, 0) if (dep[fa[i][t]] > dep[y]) t = fa[i][t];	
			
			ansx += query(rt[0], rt[st[t] - 1], 1, n, st[x], ed[x]);	
			ansx += query(rt[st[x] - 1], rt[ed[x]], 1, n, st[1], st[t] - 1);
			ansx += query(rt[ed[t]], rt[n], 1, n, st[x], ed[x]);
			ansx += query(rt[st[x] - 1], rt[ed[x]], 1, n, ed[t] + 1, n);

		} else {

			ansx += query(rt[st[x] - 1], rt[ed[x]], 1, n, st[y], ed[y]);
			ansx += query(rt[st[y] - 1], rt[ed[y]], 1, n, st[x], ed[x]);
		}
		-- ansx;
	}

	ansy = 1ll * m * (m - 1) / 2;
	ll gg = __gcd(ansx, ansy); ansx /= gg, ansy /= gg;
	printf("%lld/%lld
", ansx, ansy);

	cerr << 1.0 * clock() / CLOCKS_PER_SEC << endl;
	return 0;
}

原文地址:https://www.cnblogs.com/LSTete/p/9532215.html