题意

给你 \(n\) 个武器,\(m\) 个敌人,问你最多消灭多少个敌人,并输出方案。

总共有三种武器。

  • SQL 火箭 - 能消灭给你集合中的一个敌人 \(\sum |S| \le 100000\) ;
  • 认知光束 - 可以消灭 \([l, r]\) 区间中的一个敌人;
  • OMG 火箭筒 - 消灭给你集合中的 \(0\) 个或者 \(2\) 个敌人,集合大小为 \(3\) ,且火箭筒消灭的集合互不重合。

\(n, m \le 5000\) 。

题解

现场的时候直觉告诉我是网络流,但是这个数据范围,以及 CodeForces 从不出网络流的传言,不敢刚。。

其实这题还是个网络流,因为可以看出来还是个是个二分图求最大匹配的模型(对于前两种武器来说)。

首先对于第一种武器,可以直接在二分图上暴力连边。

第二种武器,我们可以考虑线段树优化建边就行了。(就是树上的边从父亲向儿子连 \(inf\) 就行了)

第三种武器,看起来只有 \(0,2\) 两种取值很奇怪,其实我们可以把它当成有 \(0,1,2\) 三种取值。

也就是说这个点可以匹配 \(0\sim 2\) 个点。

为什么取 \(1\) 时候是对的呢?因为我们总可以在集合中找另外一个点,把原来消灭它的武器换成这个武器就行了。

然后就可以建完了,至于时间复杂度 ,题解给了一个 \(O(nm \log m)\) 的复杂度,其实我觉得是 \(O(flow)\) 。。


然后输出方案有些麻烦,首先考虑前两种武器,第一种武器可以直接先匹配,第二种武器我们在线段树上自底向上依次分配可行的节点就行了,这个用一个 std :: vector 作为递归函数返回值返回值比较好写。

然后考虑第三种武器,只需要对于 \(flow = 1\) 的情况再找一个匹配了的点,替换消灭的武器就行了。

总结

然后对于一些无法取值的流量,我们可以考虑先取,然后通过构造使得这个流量满足条件。

网络流输出方案的时候考虑退流会退到最开始的哪个地方就行了。

代码

#include <bits/stdc++.h>

#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
#define All(x) (x).begin(), (x).end() using namespace std; template<typename T> inline bool chkmin(T &a, T b) {return b < a ? a = b, 1 : 0;}
template<typename T> inline bool chkmax(T &a, T b) {return b > a ? a = b, 1 : 0;} inline int read() {
int x(0), sgn(1); char ch(getchar());
for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
return x * sgn;
} void File() {
#ifdef zjp_shadow
freopen ("a.in", "r", stdin);
freopen ("a.out", "w", stdout);
#endif
} int n, m; const int inf = 0x3f3f3f3f; template<int Maxn, int Maxm>
struct Dinic { int Head[Maxn], Next[Maxm], cap[Maxm], to[Maxm], e; Dinic() { e = 1; } inline void add_edge(int u, int v, int flow) {
to[++ e] = v; Next[e] = Head[u]; Head[u] = e; cap[e] = flow;
} inline void Add(int u, int v, int flow) {
add_edge(u, v, flow); add_edge(v, u, 0);
} int S, T, dis[Maxn];
bool Bfs() {
queue<int> Q; Q.push(S); Set(dis, 0); dis[S] = 1;
while (!Q.empty()) {
int u = Q.front(); Q.pop();
for (int i = Head[u], v = to[i]; i; v = to[i = Next[i]])
if (cap[i] && !dis[v]) dis[v] = dis[u] + 1, Q.push(v);
}
return dis[T];
} int cur[Maxn];
int Dfs(int u, int flow) {
if (u == T || !flow) return flow;
int res = 0, f;
for (int &i = cur[u]; i; i = Next[i]) {
int v = to[i];
if (dis[v] == dis[u] + 1 && (f = Dfs(v, min(flow, cap[i])))) {
cap[i] -= f, cap[i ^ 1] += f, res += f;
if (!(flow -= f)) break;
}
}
if (!res) dis[u] = 0;
return res;
} int Run() {
int sum_flow = 0;
while (Bfs())
Cpy(cur, Head), sum_flow += Dfs(S, inf);
return sum_flow;
} }; const int N = 5050; Dinic<(int)1e5, (int)4e5> D; int tot; #define lson o << 1, l, mid
#define rson o << 1 | 1, mid + 1, r #define Travel(i, u, v) for(int i = D.Head[u], v = D.to[i]; i; v = D.to[i = D.Next[i]]) int ans[N], Bef; template<int Maxn>
struct Segment_Tree { int id[Maxn]; void Build(int o, int l, int r) {
if (l == r) { id[o] = l; return ; }
id[o] = ++ tot;
int mid = (l + r) >> 1;
Build(lson); Build(rson);
D.Add(id[o], id[o << 1], id[o << 1] <= m ? 1 : inf);
D.Add(id[o], id[o << 1 | 1], id[o << 1 | 1] <= m ? 1 : inf);
} inline void Connect(int o, int l, int r, int ql, int qr, int Id) {
if (ql <= l && r <= qr) { D.Add(Id, id[o], 1); return ; }
int mid = (l + r) >> 1;
if (ql <= mid) Connect(lson, ql, qr, Id);
if (qr > mid) Connect(rson, ql, qr, Id);
} vector<int> find(int o, int l, int r) {
if (l == r) return vector<int>(); vector<int> tmp;
int mid = (l + r) >> 1;
vector<int> ls = find(lson), rs = find(rson);
set_union(All(ls), All(rs), inserter(tmp, tmp.end())); Travel(i, id[o], v)
if (v <= m && !D.cap[i]) tmp.push_back(v); Travel(i, id[o], v) if (D.cap[i] && v > Bef) {
int cur = tmp[tmp.size() - 1]; tmp.pop_back();
ans[cur] = v - Bef;
}
return tmp;
} }; Segment_Tree<N << 2> T; vector<int> V1, V2, V3; int Ref[N], from[N], go[N]; void Get_Ans() {
for (int u : V1) {
Travel(i, Ref[u], v)
if (v <= m && D.cap[i ^ 1]) ans[v] = u;
}
T.find(1, 1, m); for (int u : V3) {
int flow = D.cap[from[u]];
Travel(i, Ref[u], v)
if (v != D.S && !D.cap[i]) ans[v] = u;
if (flow == 1)
Travel(i, Ref[u], v)
if (v != D.S && D.cap[i]) { ans[v] = u; break; }
}
} int main () { File(); n = read(), m = read(); tot = m; T.Build(1, 1, m); Bef = tot;
D.S = tot + n + 1; D.T = tot + n + 2; For (i, 1, m) D.Add(i, D.T, 1), go[i] = D.e - 1;
For (i, 1, n) {
int opt = read();
D.Add(D.S, Ref[i] = ++ tot, 1 + (opt == 2)); from[i] = D.e - 1;
if (opt == 0) {
int k = read(); V1.push_back(i);
while (k --) D.Add(tot, read(), 1);
} else if (opt == 1) {
int l = read(), r = read(); V1.push_back(i);
T.Connect(1, 1, m, l, r, tot);
} else {
int a = read(), b = read(), c = read();
V3.push_back(i);
D.Add(tot, a, 1);
D.Add(tot, b, 1);
D.Add(tot, c, 1);
}
} printf ("%d\n", D.Run()); Get_Ans(); For (i, 1, m) if (ans[i])
printf ("%d %d\n", ans[i], i); return 0; }

Codeforces 1045. A. Last chance(网络流 + 线段树优化建边)的更多相关文章

  1. 2018.09.27 codeforces1045A. Last chance(线段树优化建图+最大流)

    传送门 看完题应该都知道是网络流了吧. 但是第二种武器直接建图会gg. 因此我们用线段树优化建图. 具体操作就是,对于这m个人先建一棵线段树,父亲向儿子连容量为inf的边,最后叶子结点向对应的人连容量 ...

  2. Codeforces 1045A Last chance 网络流,线段树,线段树优化建图

    原文链接https://www.cnblogs.com/zhouzhendong/p/CF1045A.html 题目传送们 - CF1045A 题意 你有 $n$ 个炮,有 $m$ 个敌人,敌人排成一 ...

  3. [Codeforces 1197E]Culture Code(线段树优化建图+DAG上最短路)

    [Codeforces 1197E]Culture Code(线段树优化建图+DAG上最短路) 题面 有n个空心物品,每个物品有外部体积\(out_i\)和内部体积\(in_i\),如果\(in_i& ...

  4. 【BZOJ3681】Arietta 树链剖分+可持久化线段树优化建图+网络流

    [BZOJ3681]Arietta Description Arietta 的命运与她的妹妹不同,在她的妹妹已经走进学院的时候,她仍然留在山村中.但是她从未停止过和恋人 Velding 的书信往来.一 ...

  5. CF786B Legacy 线段树优化建图 + spfa

    CodeForces 786B Rick和他的同事们做出了一种新的带放射性的婴儿食品(???根据图片和原文的确如此...),与此同时很多坏人正追赶着他们.因此Rick想在坏人们捉到他之前把他的遗产留给 ...

  6. G. 神圣的 F2 连接着我们 线段树优化建图+最短路

    这个题目和之前写的一个线段树优化建图是一样的. B - Legacy CodeForces - 787D 线段树优化建图+dij最短路 基本套路 之前这个题目可以相当于一个模板,直接套用就可以了. 不 ...

  7. 一个神秘的oj2587 你猜是不是dp(线段树优化建图)

    哇 这难道不是happiness的翻版题嘛? 从\(S\)向一个点连染成白色的收益 从这个点向\(T\)连染成黑色的收益 对于额外的收益,建一个辅助点,跟区间内的每个点连\(inf\),然后向S/T, ...

  8. UOJ#77. A+B Problem [可持久化线段树优化建边 最小割]

    UOJ#77. A+B Problem 题意:自己看 接触过线段树优化建图后思路不难想,细节要处理好 乱建图无果后想到最小割 白色和黑色只能选一个,割掉一个就行了 之前选白色必须额外割掉一个p[i], ...

  9. BZOJ5017 [SNOI2017]炸弹 - 线段树优化建图+Tarjan

    Solution 一个点向一个区间内的所有点连边, 可以用线段树优化建图来优化 : 前置技能传送门 然后就得到一个有向图, 一个联通块内的炸弹可以互相引爆, 所以进行缩点变成$DAG$ 然后拓扑排序. ...

随机推荐

  1. PHP实用代码片段(二)

    1. 转换 URL:从字符串变成超链接 如果你正在开发论坛,博客或者是一个常规的表单提交,很多时候都要用户访问一个网站.使用这个函数,URL 字符串就可以自动的转换为超链接. function mak ...

  2. 单例模式及设计url分发

      1.单例模式 2.admin源码解析 3.注册源码流程图 3.admin之url方法的使用 4.admin源码之url设计 5.设计url源码流程 6.总结 1.单例模式 https://www. ...

  3. API接口TOKEN设计

    首先需要知道API是什么?   API(Application Programming Interface)即应用程序接口.你可以认为 API 是一个软件组件或是一个 Web 服务与外界进行的交互的接 ...

  4. day 7-13 数据库的数据类型

    一. 数据类型 存储引擎决定了表的类型,而表内存放的数据也要有不同的类型,每种数据类型都有自己的宽度,但宽度是可选的 注意:int类型的宽度是显示宽度,并非是数据的存储宽度 详细的介绍:http:// ...

  5. C# Note20: 制作延时改变显示的标题栏

    前言 在使用wpf构建一个窗体时,其中有这样一个功能,在保存数据或加载数据时,我们希望在改变标题栏的显示以标志当前保存成功的状态或者加载数据的名称信息,而且标题信息更新显示几秒后,再恢复到默认的状态. ...

  6. MyBatis映射文件3(参数处理Map)

    参数命名 POJO 如果多个参数,正好是业务逻辑的数据模型,那么我们就可以直接传入POJO,这样#{}中就可以直接使用属性名 Map 如果多个参数不是业务逻辑的数据模型,没有对应的POJO,为了方便, ...

  7. 索引使用,分析初探。(explain分析执行计划,以及强制使用force index)

    促使这次探索的初衷还是因为要对一个定时脚本性能进行优化. 脚本有两个指定状态分别是status, latest_process_status,和一个超期时间expire_time进行限制. 按照我以前 ...

  8. python函数、模块、包

    一.函数 定义函数: def fun_name(para_list): coding def fun_name(para_list): coding return xxx 使用函数,fun_name( ...

  9. python数据结构与算法第八天【冒泡排序】

    1.排序算法的稳定性 稳定排序算法会让原本有相同键值的记录维持相对次序 例如:对以下元组按照元组的第一个元素升序排列,元组如下: (4,1) (3,1) (3,7) (5,6) 若要满足条件,则可能的 ...

  10. 解决mybatis generator警告Cannot obtain primary key information from the database, generated objects may be incomplete

    使用 mybatis generator 生成pojo.dao.mapper时 经常出现 Cannot obtain primary key information from the database ...