@wsndy-xx
2019-08-13T23:51:56.000000Z
字数 4416
阅读 912
多做几套模拟题,体会心路历程。
time 8.13
这套题的三道题不是同一时间做的,毕竟也没有机会创造 3.5 h。
输入 m 个区间 [x, y],你希望从中选择尽量多的区间,使得两两不相交,也不能有公共端点。
其中 m <= 100000, x,y <= 1e9
算法分析
from mjt
对区间端点离散化后直接 n*logn 的 dp
from lyq
考虑不离散化的做法,直接对区间进行二分是错误的,错误也非常明显,无论怎么排序,然而对于一个区间 q,如果 q 完全包含于区间 p,显然 p 是无用的。因此首先将无用区间剔除,就可以非常简单的二分得到想要的。
得分分析
我可以在20分钟内实现一个70分的做法,同样有信心在 2h 内解决这道题
如果放在一年前同样不知道是否可以用更短的时间解决这道题,因为这道题涉及自己最不擅长的 dp
代码
#include <bits/stdc++.h>using namespace std;const int N = 101000;struct Node {int l, r;bool notuse;} A[N];int f[N];int maxn[N];Node B[N];int js;bool Cmp(Node a, Node b) {if(a.l == b.l) return a.r < b.r;return a.l < b.l;}int n;int Find(int R, int x) {int l = 1, r = R, Ans = 1;while(l <= r) {int mid = (l + r) >> 1;if(A[mid].r < x) Ans = mid, l = mid + 1;else r = mid - 1;}return Ans;}int main() {cin >> n;for(int i = 1; i <= n; i ++) cin >> A[i].l >> A[i].r;sort(A + 1, A + n + 1, Cmp);int l = 1;for(int i = 1; i <= n; i ++)for(int j = i + 1; j <= n; j ++)if(A[j].l > A[i].l && A[j].r < A[i].r) {A[i].notuse = 1;break;}for(int i = 1; i <= n; i ++) if(A[i].notuse == 0) B[++ js] = A[i];n = js;for(int i = 1; i <= n; i ++) A[i] = B[i];for(int i = 1; i <= n; i ++) {if(i == 1) {maxn[i] = 1;f[1] = 1; continue;}int wei = Find(i - 1, A[i].l);if(A[wei].r < A[i].l) {f[i] = maxn[wei] + 1;maxn[i] = max(maxn[i - 1], f[i]);} else {f[i] = 1;maxn[i] = max(maxn[i - 1], 1);}}int ans = 0;for(int i = 1; i <= n; i ++) ans = max(ans, f[i]);cout << ans;return 0;}
输入一棵n个点的树,每条边的长度是1,
一共q次询问,每次询问k个点,
B君希望在树上保留最少的边,使得询问给出的k个点是连通的。
输出最小的边数
其中 1<=n<=1e5,1<=q<=1e5,2<=k<=4
算法分析
对于 k=2 的数据直接输出树上两点间的距离。
对于全体数据
对任意两点之间从树上抽离出区间路径,然后类似线段求交统计答案
显然区间路径的建立是依靠树链剖分来实现的。
时间复杂度 q * logn * logn * C(不大的常数)??
特别注意的是边权向点权的转化,一直没想到。
得分分析
可以在 20 分钟内拿下 30 分
可以在 2h 内解决这道题
不过要是放在一年前,感觉自己会非常快的想到这道题自己的正解,并且在 1.5h 甚至 1h 就可以实现这道题的正解,毕竟这种树上路径抽离的题目自己以前接触过。岁月不饶人,放在现在是不会那么快的就想到了。
代码
#include <bits/stdc++.h>using namespace std;const int N = 1e5 + 10;#define gc getchar()inline int read() {int x = 0; char c = gc;while(c < '0' || c > '9') c = gc;while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = gc;return x;}int now = 1, n, q;int head[N];struct Node {int v, nxt;} G[N << 1];void Add(int u, int v) {G[now].v = v, G[now].nxt = head[u]; head[u] = now ++;}int son[N], size[N], topp[N], deep[N], fa[N];int tree[N], tjs;int a[10];void Dfs1(int u, int f, int dep) {deep[u] = dep;size[u] = 1;fa[u] = f;for(int i = head[u]; ~ i; i = G[i].nxt) {int v = G[i].v;if(v != fa[u]) {Dfs1(v, u, dep + 1);size[u] += size[v];if(size[son[u]] < size[v]) son[u] = v;}}}void Dfs2(int u, int tp) {tree[u] = ++ tjs;topp[u] = tp;if(!son[u]) return ;Dfs2(son[u], tp);for(int i = head[u]; ~ i; i = G[i].nxt) {int v = G[i].v;if(v != fa[u] && v != son[u]) Dfs2(v, v);}}struct Seg {int l, r;} seg[N * 4];int segjs;void Lca(int x, int y) {if(x == y) return ;int tp1 = topp[x], tp2 = topp[y];while(tp1 != tp2) {if(deep[tp1] < deep[tp2]) swap(x, y), swap(tp1, tp2);seg[++ segjs].l = tree[tp1], seg[segjs].r = tree[x];x = fa[tp1];tp1 = topp[x];}if(tree[x] == tree[y]) return ;if(tree[x] < tree[y]) swap(x, y);seg[++ segjs].l = tree[y] + 1;seg[segjs].r = tree[x];}bool Cmp(Seg a, Seg b) {if(a.l == b.l) return a.r < b.r;else return a.l < b.l;}Seg A[N * 4];int ansjs;int main() {n = read();for(int i = 1; i <= n; i ++) head[i] = -1;for(int i = 1; i <= n - 1; i ++) {int u = read(), v = read();Add(u, v), Add(v, u);}Dfs1(1, 0, 0);Dfs2(1, 1);q = read();for(; q; q --) {int s = read();segjs = 0;for(int i = 1; i <= s; i ++) a[i] = read();for(int i = 1; i <= s; i ++) {for(int j = i + 1; j <= s; j ++) {Lca(a[i], a[j]);}}if(segjs == 0) {cout << 0 << "\n";continue;}sort(seg + 1, seg + segjs + 1, Cmp);ansjs = 1;A[1].l = seg[1].l, A[1].r = seg[1].r;for(int i = 2; i <= segjs; i ++) {if(seg[i].l <= seg[i - 1].r) {A[ansjs].r = max(A[ansjs].r, seg[i].r);} else {ansjs ++;A[ansjs].l = seg[i].l, A[ansjs].r = seg[i].r;}}int ans = 0;for(int i = 1; i <= ansjs; i ++) {if(A[i].l == A[i].r && A[i].l == 0) continue;ans += A[i].r - A[i].l + 1;}cout << ans << "\n";}return 0;}
输入 m 个区间,你希望从中选择尽量多的区间,使得两两不相交,也不能有公共端点。
假设输入的区间的 xi 和 yi 是 1 到 n 之间的整数,输入的区间不能有重复的。
输入的区间是没有顺序的(即输入[1,1] [2, 2] 和输入[2,2] [1, 1] 算作一种方案)
问有多少种选择这 m 个区间的方式,使得原问题的解,答案为 l 。
因为方案数很大,输出对 10007 取模的结果即可。
其中 n <= 50
得分分析:
鄙人只会 30 分暴力,现在的水平调试了 1h
啊! 真笨。
不过有早就该收获的新收获。
代码
#include <bits/stdc++.h>using namespace std;const int N = 1e5 + 10;int n, m, l_;struct Node {int l, r;} a[N];int js;bool vis[N];int ans;Node b[N];int tot;bool Cmp(Node aa, Node bb) {if(aa.l == bb.l) {return aa.r < bb.r;}return aa.l < bb.l;}int f[N];bool Check() {tot = 0;for(int i = 1; i <= js; i ++) {if(vis[i] == 1) {b[++ tot] = a[i];}}sort(b + 1, b + tot + 1, Cmp);for(int i = 1; i <= tot; i ++) f[i] = 1;for(int i = 2; i <= tot; i ++) {for(int j = 1; j < i; j ++) {if(b[i].l > b[j].r) {f[i] = max(f[i], f[j] + 1);}}}int seg = 0;for(int i = 1; i <= tot; i ++) seg = max(seg, f[i]);if(seg == l_) return 1;return 0;}void Dfs(int x, int step) {if(step == m) {if(Check()) ans ++;return ;}int i = x + 1;if(i == js + 1) return ;vis[i] = 1;Dfs(i, step + 1);vis[i] = 0;Dfs(i, step);}int main() {cin >> n >> m >> l_;for(int i = 1; i <= n; i ++) {for(int j = i; j <= n; j ++) {a[++ js].l = i;a[js].r = j;}}Dfs(0, 0);cout << ans;return 0;}
总结:如果只写暴力可以在 20min + 20min + 60min = 100min 得到 70 + 30 + 30 = 130 分。
稍加思考可以在 120min + 30min + 60min = 3.5h 得到 100 + 30 + 30 = 160 分
目前的能力极限: 100 + 100 + 30 = 230 分
总体来说,个人认为难度 > noip2018 Day1