2018冬令营模拟测试赛(十九)
2018冬令营模拟测试赛(十九)
[Problem A]小Y
试题描述
输入
见“试题描述”
输出
见“试题描述”
输入示例
见“试题描述”
输出示例
见“试题描述”
数据规模及约定
见“试题描述”
题解
目前未知。
这题目前就能做到 \(O(n \sqrt{M} \log n)\),其中 \(M\) 是逆序对数,然而会被卡 T;当然这题暴力可以拿到和左边那个算法一样的分数,只要暴力加一个剪枝:当左区间最大值小于右区间最小值时就直接输出阶乘。
// 略(不要打我 TAT)
[Problem B]暗牧
试题描述
输入
见“试题描述”
输出
见“试题描述”
输入示例
见“试题描述”
输出示例
见“试题描述”
数据规模及约定
见“试题描述”
题解
这题要用到的节点不多,自然想到构建虚树。把所有边和询问所提到的点都作为关键点,每棵树分别处理,这样能把点数压成 \(4(m+q)\),边数也是这个级别的,最后跑 \(q\) 遍最短路就好了。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <vector>
#include <queue>
#include <cassert>
using namespace std;
#define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
#define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)
int read() {
int x = 0, f = 1; char c = getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
#define maxtrn 300010
#define maxlog 19
#define maxom 100010
#define maxn 400050
#define maxm 1000090
#define ool (1ll << 60)
#define LL long long
#define pii pair <int, int>
#define x first
#define y second
#define mp(x, y) make_pair(x, y)
int ToT;
struct Graph {
int m, head[maxn], nxt[maxm], to[maxm], dist[maxm];
void AddEdge(int a, int b) {
to[++m] = b; nxt[m] = head[a]; head[a] = m;
swap(a, b);
to[++m] = b; nxt[m] = head[a]; head[a] = m;
return ;
}
void AddEdge(int a, int b, int c) {
assert(a >= 0 && b >= 0);
// printf("edge %d %d, %d\n", a, b, c);
to[++m] = b; dist[m] = c; nxt[m] = head[a]; head[a] = m;
swap(a, b);
to[++m] = b; dist[m] = c; nxt[m] = head[a]; head[a] = m;
return ;
}
} tr, G;
int fa[maxtrn][maxlog], dep[maxtrn], dfn[maxtrn], clo;
void build(int u) {
dfn[u] = ++clo;
rep(i, 1, maxlog - 1) fa[u][i] = fa[fa[u][i-1]][i-1];
for(int e = tr.head[u]; e; e = tr.nxt[e]) if(tr.to[e] != fa[u][0]) {
fa[tr.to[e]][0] = u;
dep[tr.to[e]] = dep[u] + 1;
build(tr.to[e]);
}
return ;
}
int lca(int a, int b) {
if(dep[a] < dep[b]) swap(a, b);
dwn(i, maxlog - 1, 0) if(dep[a] - dep[b] >= (1 << i)) a = fa[a][i];
dwn(i, maxlog - 1, 0) if(fa[a][i] != fa[b][i]) a = fa[a][i], b = fa[b][i];
return a == b ? a : fa[b][0];
}
struct Edge {
int p1, u1, p2, u2;
Edge() {}
Edge(int _1, int _2, int _3, int _4): p1(_1), u1(_2), p2(_3), u2(_4) {}
} es[maxom], qs[15];
int num[(maxom<<1)+20], cntn, gnode[maxtrn<<1];
vector <int> vnode[(maxom<<1)+20];
const bool cmp(const int& a, const int& b) {
return dfn[a] < dfn[b];
}
const bool cmpequ(const int& a, const int& b) {
return dfn[a] == dfn[b];
}
const int HMOD = 1000037;
struct Hash {
int tot, head[HMOD], nxt[maxn], id[maxn];
pii key[maxn];
void Insert(pii a, int v) {
// printf("insert: pair(%d, %d) %d\n", a.x, a.y, v);
int u = (a.x * 233 + a.y) % HMOD;
nxt[++tot] = head[u]; id[tot] = v; key[tot] = a; head[u] = tot;
return ;
}
int Find(pii a) {
int u = (a.x * 233 + a.y) % HMOD;
for(int e = head[u]; e; e = nxt[e]) if(key[e].x == a.x && key[e].y == a.y) return id[e];
return -1;
}
} hh;
struct Node {
int u; LL d;
Node() {}
Node(int _, LL __): u(_), d(__) {}
bool operator < (const Node& t) const { return d > t.d; }
};
LL d[maxn];
bool vis[maxn];
priority_queue <Node> Q;
LL ShortPath(int s, int t) {
rep(i, 1, ToT) d[i] = ool;
memset(vis, 0, sizeof(vis));
d[s] = 0;
Q.push(Node(s, 0));
while(!Q.empty()) {
int u = Q.top().u; Q.pop();
if(vis[u]) continue;
vis[u] = 1;
for(int e = G.head[u]; e; e = G.nxt[e]) if(d[G.to[e]] > d[u] + G.dist[e]) {
d[G.to[e]] = d[u] + G.dist[e];
if(!vis[G.to[e]]) Q.push(Node(G.to[e], d[G.to[e]]));
}
}
return d[t];
}
int main() {
int n = read(), m = read(), q = read();
rep(i, 1, n - 1) {
int a = read(), b = read();
tr.AddEdge(a, b);
}
build(1);
rep(i, 1, m) {
int p1 = read(), u1 = read(), p2 = read(), u2 = read();
es[i] = Edge(p1, u1, p2, u2);
num[++cntn] = u1; num[++cntn] = u2;
}
rep(i, 1, q) {
int p1 = read(), u1 = read(), p2 = read(), u2 = read();
qs[i] = Edge(p1, u1, p2, u2);
num[++cntn] = u1; num[++cntn] = u2;
}
sort(num + 1, num + cntn + 1);
cntn = unique(num + 1, num + cntn + 1) - num - 1;
rep(i, 1, m) {
es[i].u1 = lower_bound(num + 1, num + cntn + 1, es[i].u1) - num;
es[i].u2 = lower_bound(num + 1, num + cntn + 1, es[i].u2) - num;
// printf("%d <- %d; %d <- %d\n", es[i].u1, es[i].p1, es[i].u2, es[i].p2);
vnode[es[i].u1].push_back(es[i].p1);
vnode[es[i].u2].push_back(es[i].p2);
}
rep(i, 1, q) {
qs[i].u1 = lower_bound(num + 1, num + cntn + 1, qs[i].u1) - num;
qs[i].u2 = lower_bound(num + 1, num + cntn + 1, qs[i].u2) - num;
// printf("%d <- %d; %d <- %d\n", qs[i].u1, qs[i].p1, qs[i].u2, qs[i].p2);
vnode[qs[i].u1].push_back(qs[i].p1);
vnode[qs[i].u2].push_back(qs[i].p2);
}
rep(i, 1, cntn) {
sort(vnode[i].begin(), vnode[i].end(), cmp);
vector <int> :: iterator it = unique(vnode[i].begin(), vnode[i].end(), cmpequ);
vnode[i].erase(it, vnode[i].end());
// printf("tree %d:", i); for(auto j : vnode[i]) printf(" %d", j); putchar('\n');
int cntg = 0;
rep(j, 0, vnode[i].size() - 1) {
gnode[++cntg] = vnode[i][j];
if(j < vnode[i].size() - 1) gnode[++cntg] = lca(vnode[i][j], vnode[i][j+1]);
}
sort(gnode + 1, gnode + cntg + 1, cmp);
cntg = unique(gnode + 1, gnode + cntg + 1, cmpequ) - gnode - 1;
rep(j, 1, cntg) hh.Insert(mp(i, gnode[j]), ++ToT);
rep(j, 1, cntg)
if(j > 1) {
int a = lca(gnode[j-1], gnode[j]);
G.AddEdge(hh.Find(mp(i, a)), hh.Find(mp(i, gnode[j])), dep[gnode[j]] - dep[a]);
}
}
rep(i, 1, m) G.AddEdge(hh.Find(mp(es[i].u1, es[i].p1)), hh.Find(mp(es[i].u2, es[i].p2)), 1);
rep(i, 1, q) {
int p1 = qs[i].p1, u1 = qs[i].u1, p2 = qs[i].p2, u2 = qs[i].u2;
LL ans = ShortPath(hh.Find(mp(u1, p1)), hh.Find(mp(u2, p2)));
if(ans < ool) printf("%lld\n", ans); else puts("impossible");
}
return 0;
}
[Problem C]大根
试题描述
输入
见“试题描述”
输出
见“试题描述”
输入示例
见“试题描述”
输出示例
见“试题描述”
数据规模及约定
见“试题描述”
题解
这题可以转化成生成 \(k\) 个不相交区间,要求每个元区间都包含至少一个生成的区间,且生成的区间至少被一个原区间包含。
于是变成两种贪心取最优:
-
将包含别的区间的区间放到集合 \(B\) 中,否则放到集合 \(A\) 中,然后枚举 \(A\) 中分 \(k_1\) 类,那么 \(B\) 中选择 \(k - k_1\) 个。\(B\) 中选的一定是单个区间。这样能保证合法(想想为什么)。
-
按长度排序取前 \(k-1\) 个,然后剩下的所有分一类。(这就是在上面有区间集合的交集为 \(0\) 的时候这种贪心才会起作用)
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
#define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)
int read() {
int x = 0, f = 1; char c = getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
#define maxn 1000010
#define LL long long
struct Line {
int l, r;
Line() {}
Line(int _, int __): l(_), r(__) {}
bool operator == (const Line& t) const { return l == t.l && r == t.r; }
bool operator < (const Line& t) const { return r != t.r ? r < t.r : l > t.l; }
Line operator * (const Line& t) const { return Line(max(l, t.l), min(r, t.r)); }
Line operator *= (const Line& t) { *this = *this * t; return *this; }
} ls[maxn], getl[maxn], bigl[maxn];
int n, K, cntg, cntb, val[maxn];
bool cmplen(Line a, Line b) { return a.r - a.l > b.r - b.l; }
int main() {
n = read(); K = read();
rep(i, 1, n) {
int l = read(), r = read();
ls[i] = Line(l, r);
}
sort(ls + 1, ls + n + 1);
for(int i = 1; i <= n; ) {
getl[++cntg] = ls[i++];
while(i <= n && ls[i] * ls[i-1] == ls[i-1]) bigl[++cntb] = ls[i++];
}
sort(bigl + 1, bigl + cntb + 1, cmplen);
sort(getl + 1, getl + cntg + 1);
LL sum = 0, ans = 0;
rep(i, 1, cntg) sum += getl[i].r - getl[i].l;
rep(i, 1, cntg - 1) val[i] = getl[i+1].r - getl[i].l;
sort(val + 1, val + cntg), reverse(val + 1, val + cntg);
dwn(i, cntg - 1, K) sum -= val[i];
rep(i, cntg + 1, K) sum += bigl[i-cntg].r - bigl[i-cntg].l;
int cg, cb;
if(cntg <= K) cg = cntg, cb = K - cntg;
else cg = K, cb = 0;
ans = max(ans, sum);
for(; cg > 1;) {
sum -= val[--cg];
cb++; sum += bigl[cb].r - bigl[cb].l;
ans = max(ans, sum);
}
sum = 0;
sort(ls + 1, ls + n + 1, cmplen);
rep(i, 1, K - 1) sum += ls[i].r - ls[i].l;
printf("%lld\n", max(ans, sum));
return 0;
}