题意

给你 \(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. Day3 Python基础之while、for循环(二)

    1.数据运算 算数运算 整除运算:// 取余运算:% 指数运算:** 赋值运算 b+=a;等价于b=b+a 比较运算 >,<,==,!=,>=,<= 逻辑运算符 and .or ...

  2. MySQL分页时统计总记录行数并使用limit返回固定数目的记录

    需求很简单:假设有一个user表,表中实际上有10000条数据,但是我不知道有多少条,我要从数据库中每次取20条数据显示,那么怎么完成呢? 方案一: 首先执行一个 select count(*) as ...

  3. jmeter内存溢出解决办法

    原文:http://blog.51cto.com/xqtesting/2107927 使用jmeter进行压力测试时遇到一段时间后报内存溢出outfmenmory错误,导致jmeter卡死了,先尝试在 ...

  4. Mysql占用CPU过高如何优化?(转)

    原文:http://bbs.landingbj.com/t-0-241441-1.html MySQL处在高负载环境下,磁盘IO读写过多,肯定会占用很多资源,必然CP会U占用过高. 占用CPU过高,可 ...

  5. IdentityServer4【QuickStart】之利用OpenID Connect添加用户认证

    利用OpenID Connect添加用户认证 利用OpenID Connect添加用户认证 在这个示例中我们想要通过OpenID Connect协议将交互用户添加到我们的IdentityServer上 ...

  6. java lang(Comparable接口) 和java util(Comparator接口)分析比较

    //Comparable 接口强行对实现它的每个类的对象进行整体排序. -- 自然排序.类的compareTo称为自然比较方法. public interface Comparable<T> ...

  7. Appscanner实验还原code1

    import _pickle as pickle from sklearn import svm, ensemble import random from sklearn.metrics import ...

  8. WPF当属性值改变时利用PropertyChanged事件来加载动画

    在我们的程序中,有时我们需要当绑定到UI界面上的属性值发生变化从而引起数据更新的时候能够加载一些动画,从而使数据更新的效果更佳绚丽,在我们的程序中尽量将动画作为一种资源放在xaml中,而不是在后台中通 ...

  9. 如何在MAC上运行exe程序

    1. 首先下载并运行CrossOver 运行CrossOver需要收费,试用期为14天,运行CrossOver 2. 选择exe应用程序,新建容器,安装exe程序 3.安装成功后,运行exe应用程序启 ...

  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 ...