前言

线段树+网络最大流的建模题。

博客园食用更佳

题目大意

最初时有 \(n\) 个 \(1\) 。给定 \(op\) 、 \(l\) ,其中, \(l\) 为操作次数上限。你有四个操作:

  1. 若 \(op=1\) ,则接下来两个整数 \(a,b\) ,表示可以将 \(a\) 变为 \(b\) 。
  2. 若 \(op=2\) ,则接下来三个整数 \(a_1,a_2,b_1\) ,表示可以将范围在 \(a_1\) 到 \(a_2\) 的任意的数变为 \(b_1\) 。
  3. 若 \(op=3\) ,则接下来三个整数 \(a_1,b_1,b_2\) ,表示可以将 \(a_1\) 变为范围在 \(b_1\) 到 \(b_2\) 的任意的数。
  4. 若 \(op=4\) ,则接下来四个整数 \(a_1,a_2,b_1,b_2\) ,表示可以将范围在 \(a_1\) 到 \(a_2\) 的任意的数变为范围在 \(b_1\) 到 \(b_2\) 的任意的数。

问最后能有多少个数字变为 \(k\) ,其中 \(1<=a,b,a1,b1,a2,b2<=k\) 。

思路

首先用暴力建图跑网络流。如果看出使用网络流,则建图就变得非常简单了。

把所有单个数字的查询都看为一个区间,那么四个操作都将是区间的连边。

使用一个类似于中转站的两个节点记为 \(tmp1\) 和 \(tmp2\) ,将 \(a\) 区间的所有值连向 \(tmp1\) ,将 \(tmp2\) 连向 \(b\) 区间的所有值,容量为无穷大。则 \(tmp1\) 到 \(tmp2\) 连一条容量为 \(l\) 的边用来限制操作次数。

跑最大流即可得出答案。暴力建图伪代码:

int tot = k;
int opt, l;
for(int i = 1; i <= m; i++) {
scanf("%d %d", &opt, &l);
if(opt == 1) {
int a, b;
scanf("%d %d", &a, &b);
Addedge(a, b, l);
}
else if(opt == 2) {
int a1, a2, b1;
scanf("%d %d %d", &a1, &a2, &b1);
int tmp1 = ++tot;
int tmp2 = ++tot;
for(int i = a1; i <= a2; i++)
Addedge(i, tmp1, INF);
Addedge(tmp2, b1, INF);
Addedge(tmp1, tmp2, l);
}
else if(opt == 3) {
int a1, b1, b2;
scanf("%d %d %d", &a1, &b1, &b2);
int tmp1 = ++tot;
int tmp2 = ++tot;
Addedge(a1, tmp1, INF);
for(int i = b1; i <= b2; i++)
Addedge(tmp2, i, INF);
Addedge(tmp1, tmp2, l);
}
else {
int a1, a2, b1, b2;
scanf("%d %d %d %d", &a1, &a2, &b1, &b2);
int tmp1 = ++tot;
int tmp2 = ++tot;
for(int i = a1; i <= a2; i++)
Addedge(i, tmp1, INF);
for(int i = b1; i <= b2; i++)
Addedge(tmp2, i, INF);
Addedge(tmp1, tmp2, l);
}
}
t = tot + 1;
Addedge(s, 1, n);
Addedge(k, t, INF);

显然,一次操作会产生最多 \(2k+2\) 条边,在观察这个数据范围,过不了。亲测 50 Pts。可能是常数太大。。。

由于是区间操作,可以想到使用数据结构来优化建图。

使用线段树,建立两颗线段树,如下图。

\(s\) 连向第一棵线段树的 \([1,1]\) 区间的点,容量为 \(n\) ,第二棵线段树的 \([k,k]\) 区间的点连向 \(t\) ,容量为极大值,和暴力差不多。

可以把第二棵树理解为是操作树,是用来进行操作的。第一棵树的儿子连向自己的父亲,方便选定被操作前的范围。第二棵的父亲连向自己的儿子,方便选定操作后的范围。以上边的容量均为极大值。

最后第二棵树的节点连向第一棵树的对应点,方便操作后被再次操作。

最后跑一边最大流 Dinic ,在随机图上 Dinic 普遍优于其他 \(O(nmlog(m))\) 的算法。

Code

#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f
const int MAXN = 1e6 + 5;
const int MAXM = 5e6 + 5;
struct Segment_Tree {
int Left_Section, Right_Section, Data;
#define LC(x) (x << 1)
#define RC(x) (x << 1 | 1)
#define L(x) tree[0][x].Left_Section
#define R(x) tree[0][x].Right_Section
#define D(x, y) tree[y][x].Data
};
Segment_Tree tree[2][MAXN];
struct Edge {
int Next, To, Cap;
};
Edge edge[MAXM << 1];
int head[MAXM << 1];
int edgetot = 1;
int tot;
int n, m, k, s, t;
queue<int> q;
int dep[MAXN], stt[MAXN];
int Begin, End;
void Addedge(int x, int y, int z) {
edge[++edgetot].Next = head[x], edge[edgetot].To = y, edge[edgetot].Cap = z, head[x] = edgetot;
edge[++edgetot].Next = head[y], edge[edgetot].To = x, edge[edgetot].Cap = 0, head[y] = edgetot;
}
void Build(int pos, int l, int r, int flag) {//初始化线段树的节点信息
D(pos, flag) = ++tot;//开辟新的节点
L(pos) = l;//初始化左区间
R(pos) = r;//初始化右区间
if(l == r) {
if(flag && l == 1)//记录左树的1节点
Begin = D(pos, flag);
if(!flag && l == k)//记录右树的k节点
End = D(pos, flag);
if(!flag)//右树连左树的对应节点
Addedge(D(pos, flag), D(pos, 1 - flag), INF);
return;
}
int mid = (l + r) >> 1;
Build(LC(pos), l, mid, flag);//初始化
Build(RC(pos), mid + 1, r, flag);//同上
if(flag) {//左树儿子连父亲
Addedge(D(LC(pos), flag), D(pos, flag), INF);
Addedge(D(RC(pos), flag), D(pos, flag), INF);
}
else {//右树父亲连儿子
Addedge(D(pos, flag), D(LC(pos), flag), INF);
Addedge(D(pos, flag), D(RC(pos), flag), INF);
}
}
void Query(int pos, int l, int r, int tmp, int flag) {//区间连边
if(l <= L(pos) && R(pos) <= r) {
if(flag)
Addedge(D(pos, flag), tmp, INF);//若是左树则连接中转站
else
Addedge(tmp, D(pos, flag), INF);//若是右树被中转站连接
return;
}
if(l <= R(LC(pos)))
Query(LC(pos), l, r, tmp, flag);//处理子树
if(r >= L(RC(pos)))
Query(RC(pos), l, r, tmp, flag);//同上
}
bool bfs() {//Dinic板子,不详写
for(int i = s; i <= t; i++)
dep[i] = 0;
stt[s] = head[s];
dep[s] = 1;
q.push(s);
while(!q.empty()) {
int u = q.front(); q.pop();
for(int i = head[u]; i; i = edge[i].Next) {
int v = edge[i].To;
if(!dep[v] && edge[i].Cap) {
dep[v] = dep[u] + 1;
stt[v] = head[v];
q.push(v);
}
}
}
return dep[t] != 0;
}
int dfs(int u, int flow) {//同上
if(u == t || !flow)
return flow;
int rest = flow;
for(int i = stt[u]; i && rest; i = edge[i].Next) {
stt[u] = i;
int v = edge[i].To;
if(dep[v] == dep[u] + 1 && edge[i].Cap) {
int nextflow = dfs(v, min(rest, edge[i].Cap));
if(!nextflow)
dep[v] = -1;
edge[i].Cap -= nextflow;
edge[i ^ 1].Cap += nextflow;
rest -= nextflow;
}
}
return flow - rest;
}
int Dinic() {//同上
int res = 0;
int flow;
while(bfs())
while(flow = dfs(s, INF))
res += flow;
return res;
}
int main() {
scanf("%d %d %d", &n, &m, &k);
Build(1, 1, k, 1);
Build(1, 1, k, 0);
int opt, l;
for(int i = 1; i <= m; i++) {
scanf("%d %d", &opt, &l);
int tmp1 = ++tot;
int tmp2 = ++tot;
if(opt == 1) {
int a, b;
scanf("%d %d", &a, &b);
Query(1, a, a, tmp1, 1);
Query(1, b, b, tmp2, 0);
}
else if(opt == 2) {
int a1, a2, b1;
scanf("%d %d %d", &a1, &a2, &b1);
Query(1, a1, a2, tmp1, 1);
Query(1, b1, b1, tmp2, 0);
}
else if(opt == 3) {
int a1, b1, b2;
scanf("%d %d %d", &a1, &b1, &b2);
Query(1, a1, a1, tmp1, 1);
Query(1, b1, b2, tmp2, 0);
}
else {
int a1, a2, b1, b2;
scanf("%d %d %d %d", &a1, &a2, &b1, &b2);
Query(1, a1, a2, tmp1, 1);
Query(1, b1, b2, tmp2, 0);
}
Addedge(tmp1, tmp2, l);
}
t = tot + 1;
Addedge(s, Begin, n);//连接源点到左树1的点
Addedge(End, t, INF);//连接右树k的点到汇点
printf("%d", Dinic());
return 0;
}

[题解] T'ill It's Over的更多相关文章

  1. UNR #1 题解

    A. 争夺圣杯 还是想说一下,这题是原题啊...想做的人可以戳codechef上的MTMXSUM(懒得贴链接了,套了个壳,不过正常人应该都能看得出来) 显然异或输出没什么奇怪的性质... 考虑一个元素 ...

  2. NOIP2011 题解

    铺地毯 题解:比大小 #include <cstdio> +; int n, x, y, a[MAXN], b[MAXN], g[MAXN], k[MAXN]; inline int So ...

  3. 2016多校第六场题解(hdu5793&hdu5794&hdu5795&hdu5800&hdu5802)

    这场就做出一道题,怎么会有窝这么辣鸡的人呢? 1001 A Boring Question(hdu 5793) 很复杂的公式,打表找的规律,最后是m^0+m^1+...+m^n,题解直接是(m^(n+ ...

  4. bzoj usaco 金组水题题解(2)

    续.....TAT这回不到50题编辑器就崩了.. 这里塞40道吧= = bzoj 1585: [Usaco2009 Mar]Earthquake Damage 2 地震伤害 比较经典的最小割?..然而 ...

  5. UOJ Round #1 题解

    题解: 质量不错的一套题目啊..(题解也很不错啊) t1: 首先暴力显然有20分,把ai相同的缩在一起就有40分了 然后会发现由于原来的式子有个%很不方便处理 so计数题嘛 考虑一下容斥 最终步数=初 ...

  6. 【四校联考】【比赛题解】FJ NOIP 四校联考 2017 Round 7

    此次比赛为厦门一中出题.都是聚劳,不敢恭维. 莫名爆了个0,究其原因,竟然是快读炸了……很狗,很难受. 话不多说,来看看题: [T1] 题意: 样例: PS:1<=h[i]<=100000 ...

  7. Luogu P1351 联合权值 题解

    这是一个不错的树形结构的题,由于本蒟蒻不会推什么神奇的公式其实是懒得推...,所以很愉快的发现其实只需要两个点之间的关系为祖父和儿子.或者是兄弟即可. 然后问题就变得很简单了,只需要做一个正常的DFS ...

  8. 洛谷10月月赛R2·浴谷八连测R3题解

    早上打一半就回家了... T1傻逼题不说了...而且我的写法比题解要傻逼很多T T T2可以发现,我们强制最大值所在的块是以左上为边界的倒三角,然后旋转4次就可以遍历所有的情况.所以二分极差,把最大值 ...

  9. 【Codeforces Round #404 (Div. 2)】题解

    A. Anton and Polyhedrons 直接统计+答案就可以了. #include<cstdio> #include<cstring> #include<alg ...

随机推荐

  1. NGK项目与其他项目相比有哪些优势?

    一个项目运行这么久,难免不被其他项目比来比去.NGK项目之所以能被很多人关注,是因为NGK具有独特的优势,NGK具有很高的性能,在智能合约上有多种应用,而且NGK具有独特的跨链技术.转账没有手续费,在 ...

  2. 开发工具-scala处理json格式利器-json4s

    1.为什么是json4s 从json4s的官方描述 At this moment there are at least 6 json libraries for scala, not counting ...

  3. linux 安装node和pm2

    用yum安装 curl -sL https://rpm.nodesource.com/setup_10.x | bash - yum install -y nodejs npm install -g ...

  4. 微信小程序:优化页面要渲染的属性

    问题:页面中只用到四个属性:goods_name,goods_price,goods_introduce,pics,但是整个对象中有22个属性,小程序中建议:data中只存放标签中要使用的数据,而现在 ...

  5. 前端axios传递一个包含数组的对象到后台,后台可以用String接收,也可以用List集合接收

    前端代码: data() { return { listQuery: { date: [], } }}, //查询列表信息getList() { if (this.listQuery.date == ...

  6. sql if else 用法

    语法: case when 条件1 then 结果1 when 条件2 then 结果2 else 结果N end 可以有任意多个条件,如果没有默认的结果,最后的else也可以不写, select c ...

  7. SpringBoot2.1整合Mybatis-Generator以及tk.mybatis

    1:添加依赖 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http ...

  8. web前端学习笔记(python)(一)

    瞎JB搞]感觉自己全栈了,又要把数据库里面的内容,以web形式展示出来,并支持数据操作.占了好多坑.....慢慢填(主要参考廖雪峰的官网,不懂的再百度) 一.web概念 Client/Server模式 ...

  9. 使用 Tye 辅助开发 dotnet 应用程序

    newbe.pro 已经给我们写了系列文章介绍Tye 辅助开发k8s 应用: 使用 Tye 辅助开发 k8s 应用竟如此简单(一) 使用 Tye 辅助开发 k8s 应用竟如此简单(二) 使用 Tye ...

  10. PAT-1064(Complete Binary Search Tree)JAVA实现

    Complete Binary Search Tree PAT-1064 本次因为涉及到完全二叉排序树,所以可以使用数组的形式来存储二叉排序树 对输入序列排序后,得到的是中序遍历二叉排序树的序列.对这 ...