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\n", cnt);
For(i, 1, cnt) {
printf("%d\n", 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\n", 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\n", c1);
For(i, 1, min(cnt, s - k)) for (auto v : ans[i]) printf("%d ", v); puts("");
printf("%d\n", 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\n", ansx, ansy);
cerr << 1.0 * clock() / CLOCKS_PER_SEC << endl;
return 0;
}