我是靠谱客的博主 迷你小笼包,最近开发中收集的这篇文章主要介绍2019杭电多校第三场2019杭电多校第三场,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

2019杭电多校第三场

1002. Blow up the city

upsloved

有一个DAG,出度为(0)的点是指挥中心,(q)次询问,每次给出两个点,这两个点存有关键物资,你可以炸掉一个点和它的邻接边,使得这两个点中任意一个点的物资不能到达指挥中心(有一个点不能到达任意一个指挥中心即可),求方案数,询问独立

并不会支配树,比赛的时候想到了这个东西的概念,但是不会实现,赛后学习一波

把图反过了,建一个额外的起点指向指挥中心,求出支配树,每次询问u,v就只要计算u,v以及它们的LCA的深度就好了

#include <bits/stdc++.h>
using LL = long long;
using namespace std;
const int N = 200010;
struct Node{int to,next;};
int T, n, m, q, u, v, dfn[N], clo, rev[N], f[N], semi[N], idom[N], deg[N], dep[N], fa[N][18];
struct Graph{
Node E[N << 1]; int head[N], tot;
inline void clear(){
tot = 0;
for(int i = 0; i <= n; ++i) head[i] = -1;
}
inline void link(int u, int v){
E[tot] = (Node){v, head[u]}; head[u] = tot++;
}
}pre, nxt, dom;
struct uset{
int fa[N], Mi[N];
inline void init(){
for(int i = 1; i <= n; ++i)
fa[i] = Mi[i] = semi[i] = i;
}
inline int find(int x){
if(fa[x] == x) return x;
int fx = fa[x], y = find(fa[x]);
if(dfn[semi[Mi[fx]]] < dfn[semi[Mi[x]]]) Mi[x] = Mi[fx];
return fa[x] = y;
}
}uset;
inline void tarjan(int x) {
dfn[x] = ++clo; rev[clo] = x;
for(int e = nxt.head[x]; ~e; e = nxt.E[e].next){
if(!dfn[nxt.E[e].to])
f[nxt.E[e].to] = x, tarjan(nxt.E[e].to);
}
}
inline void dfs(int x, int pre, int depth) {
dep[x] = depth;
fa[x][0] = pre;
for(int i = 1; i < 18; ++i)
fa[x][i] = fa[fa[x][i - 1]][i - 1];
for(int e = dom.head[x]; ~e; e = dom.E[e].next)
dfs(dom.E[e].to, x, depth + 1);
}
inline int LCA(int u, int v) {
if(dep[u] < dep[v]) swap(u, v);
for(int i = 17; ~i; --i) {
if(dep[fa[u][i]] >= dep[v])
u = fa[u][i];
}
if(u == v) return u;
for(int i = 17; ~i; --i) {
if(fa[u][i] != fa[v][i])
u = fa[u][i], v = fa[v][i];
}
return fa[u][0];
}
inline void calc(){
for(int i = n; i >= 2; --i){
int y = rev[i], tmp = n;
for(int e = pre.head[y]; ~e; e = pre.E[e].next){
int x = pre.E[e].to; if(!dfn[x]) continue;
if(dfn[x] < dfn[y]) tmp = min(tmp, dfn[x]);
else uset.find(x), tmp = min(tmp, dfn[semi[uset.Mi[x]]]);
}
semi[y] = rev[tmp]; uset.fa[y] = f[y];
dom.link(semi[y], y);
y = rev[i - 1];
for(int e = dom.head[y]; ~e; e = dom.E[e].next){
int x = dom.E[e].to; uset.find(x);
if(semi[uset.Mi[x]] == y) idom[x] = y;
else idom[x] = uset.Mi[x];
}
}
for(int i = 2; i <= n; ++i){
int x = rev[i];
if(idom[x] != semi[x])
idom[x] = idom[idom[x]];
}
dom.clear();
for(int i = 1; i < n; ++i)
dom.link(idom[i], i);
dfs(n, 0, 0);
}
int main() {
scanf("%d", &T);
while(T--) {
scanf("%d%d", &n, &m);
clo = 0; n++; dep[0] = -1;
for(int i = 1; i <= n; ++i)
deg[i] = dfn[i] = rev[i] = semi[i] = idom[i] = f[i] = 0;
pre.clear(); nxt.clear(); dom.clear(); uset.init();
for(int i = 1; i <= m; ++i){
scanf("%d%d", &u, &v);
pre.link(u, v);
nxt.link(v, u);
deg[u]++;
}
for(int i = 1; i < n; ++i) {
if(deg[i] == 0) {
nxt.link(n, i);
pre.link(i, n);
}
}
tarjan(n);
calc();
scanf("%d", &q);
while(q--) {
scanf("%d%d", &u, &v);
printf("%dn", dep[u] + dep[v] - dep[LCA(u, v)]);
}
}
return 0;
}

1004. Distribution of books

upsloved

有一个长为(n)的数列(A), 你需要将找(k)个飞空不相交区间,这些区间并起来是(A)的一个前缀区间,使得这(k)个区间每个区间的和的最大值最小

二分答案,线段树优化dp套路,代码不放了

1006. Fansblog

solved at 00:54

队友做的,素数分布,威尔逊定理,int128

1007. Find the answer

solved at 01:24(+3)

离散化之后线段树维护区间和以及区间数的个数

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
struct node {
int val, id;
bool operator<(const node &rhs) const {
return val < rhs.val;
}
}a[N];
int rnk[N], T, n, ans[N], m, sum, mn, b[N];
long long tree[N << 2];
int cnt[N << 2];
void pushup(int rt) {
tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];
cnt[rt] = cnt[rt << 1] + cnt[rt << 1 | 1];
}
void build(int rt, int l, int r) {
tree[rt] = 0; cnt[rt] = 0;
if(l == r) return;
int mid = l + r >> 1;
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
}
void update(int rt, int l, int r, int pos, int val) {
if(l == r) {tree[rt] += val, cnt[rt]++; return;}
int mid = l + r >> 1;
if(pos <= mid) update(rt << 1, l, mid, pos, val);
else update(rt << 1 | 1, mid + 1, r, pos, val);
pushup(rt);
}
int query(int rt, int l, int r, long long val) {
if(l == r) return tree[rt] <= val ? cnt[rt] : 0;
int mid = l + r >> 1;
if(tree[rt << 1] <= val) return cnt[rt << 1] + query(rt << 1 | 1, mid + 1, r, val - tree[rt << 1]);
else return query(rt << 1, l, mid, val);
}
int main() {
read(T);
while(T--) {
sum = ans[0] = 0;
read(n); read(m);
for(int i = 1; i <= n; ++i) {
read(a[i].val);
b[i] = a[i].val;
a[i].id = i;
}
sort(a + 1, a + n + 1);
for(int i = 1; i <= n; ++i)
rnk[a[i].id] = i;
build(1, 1, n);
for(int i = 1; i <= n; ++i) {
ans[i] = i - 1 - query(1, 1, n, m - b[i]);
update(1, 1, n, rnk[i], b[i]);
}
for(int i = 1; i <= n; ++i)
printf("%d ", ans[i]);
puts("");
}
return 0;
}

1009. K subsequence

solved at 04:42(+14)

有一个长为(n)的数列,你需要找出(k)个不相交的非降子序列使得选出的数和最大(1<=n<=2000, 1<=k<=10)

最小费用最大流经典题,拆点建图,每个点(i)拆成(i_1)(i_2)(i_1)(i_2)建容量为(1),费用为(-a_i)的边,(s)(i_1)建容量为(1),费用为(0)的边,(i_2)(t)建容量为(1),费用为(0)的边,如果(a[i]<=a[j])(i<j)(i_2)(j_1)建容量为(1),费用为(0)的边,最后(ss)(s)建容量为(k),费用为(0)的边,(ss)(t)(MCMF),最小费用取相反数就是答案

1011. Squrirrel

树型dp,待补

转载于:https://www.cnblogs.com/tusikalanse/p/11273174.html

最后

以上就是迷你小笼包为你收集整理的2019杭电多校第三场2019杭电多校第三场的全部内容,希望文章能够帮你解决2019杭电多校第三场2019杭电多校第三场所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(47)

评论列表共有 0 条评论

立即
投稿
返回
顶部