@wsndy-xx
2019-08-14T07:51:56.000000Z
字数 4416
阅读 709
多做几套模拟题,体会心路历程。
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