\(\mathcal{Description}\)

  Link.

  给一个 \(n\times n\) 的棋盘,其中 \(q\) 个互不重叠的子矩阵被禁止放棋。问最多能放多少个互不能攻击的车。

  \(n,q\le10^4\)。

\(\mathcal{Solution}\)

  如果把问题转化成“只允许在某些子矩阵上放棋”,就是一个很显然的线段树优化建图最大流。源点连向行上的线段树叶子,流量为 \(1\);行上的线段树结点向父亲连边,流量为正无穷;对于每个矩阵,行在树上分裂的 $\log $ 个区间分别向列在树上分裂的 \(\log\) 个区间连边,流量为区间长度之积;列上的线段树与行上的类似。

  于是思考怎样转变成这个问题就行了。提供一种方法:延长所有禁用矩阵的横向边,自然地把棋盘切割成若干矩形。用一条扫描线从左往右扫,维护数组 \(lef_i\) 表示第 \(i\) 行目前最右边的障碍。遇到矩阵的左边界就拿来划分,遇到右边界就更新 \(lef\),\(\mathcal O(n^2)\) 即可实现。(也可以尝试扔一个 Chtholly Tree 上去 www。)

  关于边的复杂度,一个可用矩阵所建出的边是 \(\mathcal O(\log^2n)\) 的,每个可用矩阵必然对应上下两条禁用矩阵边界的延长线,而一个禁用矩阵提供的延长线是 \(\mathcal O(1)\) 的,所以共 \(\mathcal O(q)\) 个矩形,边数总量 \(\mathcal O(n+q\log^2n)\)。

  复杂度 \(\mathcal O(\text{网络瘤})\)(大雾。

\(\mathcal{Code}\)

#include <queue>
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm> typedef std::vector<std::pair<int, int> > vecpii; const int MAXN = 1e4, INF = 0x3f3f3f3f;
int n, q; inline int rint () {
int x = 0; char s = getchar ();
for ( ; s < '0' || '9' < s; s = getchar () );
for ( ; '0' <= s && s <= '9'; s = getchar () ) x = x * 10 + ( s ^ '0' );
return x;
} namespace Dinic { const int MAXND = MAXN * 5, MAXED = 5e6; // 5e6 ?
int ecnt = 1, S, T, head[MAXND + 5], curh[MAXND + 5], d[MAXND + 5]; struct Edge { int to, flow, nxt; } graph[MAXED * 2 + 5]; inline void link ( const int s, const int t, const int f ) {
graph[++ ecnt] = { t, f, head[s] };
head[s] = ecnt;
} inline void add ( const int s, const int t, const int f ) {
#ifdef LOCAL_DEBUG
printf ( "%d %d %d\n", s, t, f );
#endif
link ( s, t, f ), link ( t, s, 0 );
} inline int DFS ( const int u, int iflow ) {
if ( u == T ) return iflow;
int oflow = 0;
for ( int& i = curh[u], v, of; i; i = graph[i].nxt ) {
if ( d[v = graph[i].to] == d[u] + 1 && graph[i].flow ) {
of = DFS ( v, std::min ( iflow, graph[i].flow ) );
oflow += of, graph[i].flow -= of, graph[i ^ 1].flow += of;
if ( ! ( iflow -= of ) ) break;
}
}
if ( ! oflow ) d[u] = -1;
return oflow;
} inline bool BFS () {
static std::queue<int> que;
for ( int i = 1; i <= T; ++ i ) d[i] = -1;
que.push ( S ), d[S] = 0;
for ( int u; ! que.empty (); que.pop () ) {
u = que.front ();
for ( int i = head[u], v; i; i = graph[i].nxt ) {
if ( ! ~ d[v = graph[i].to] && graph[i].flow ) {
que.push ( v ), d[v] = d[u] + 1;
}
}
}
return ~ d[T];
} inline int calc () {
int ret = 0;
for ( ; BFS (); ret += DFS ( S, INF ) ) {
for ( int i = 1; i <= T; ++ i ) {
curh[i] = head[i];
}
}
return ret;
} } // namespace Dinic. namespace SegmentTree { int sizt; inline int id ( const int l, const int r, const bool type ) {
return type * sizt + ( ( l + r ) | ( l != r ) );
} inline void build ( const int l, const int r, const bool type ) {
int rt = id ( l, r, type ), mid = l + r >> 1;
if ( l == r ) {
if ( ! type ) Dinic::add ( Dinic::S, rt, 1 );
else Dinic::add ( rt, Dinic::T, 1 );
return ;
}
if ( ! type ) {
Dinic::add ( id ( l, mid, type ), rt, INF );
Dinic::add ( id ( mid + 1, r, type ), rt, INF );
} else {
Dinic::add ( rt, id ( l, mid, type ), INF );
Dinic::add ( rt, id ( mid + 1, r, type ), INF );
}
build ( l, mid, type ), build ( mid + 1, r, type );
} inline void extract ( const int l, const int r, const int el, const int er, const bool type, vecpii& vec ) {
int rt = id ( l, r, type ), mid = l + r >> 1;
if ( el <= l && r <= er ) return vec.push_back ( { rt, r - l + 1 } );
if ( el <= mid ) extract ( l, mid, el, er, type, vec );
if ( mid < er ) extract ( mid + 1, r, el, er, type, vec );
} inline void init () {
sizt = id ( n, n, 0 );
Dinic::S = sizt * 2 + 3, Dinic::T = Dinic::S + 1;
build ( 1, n, 0 ), build ( 1, n, 1 );
} inline void secLink ( const int rl, const int rr, const int cl, const int cr ) {
#ifdef LOCAL_DEBUG
printf ( "sl %d %d %d %d\n", rl, rr, cl, cr );
#endif
static vecpii R, C; R.clear (), C.clear ();
extract ( 1, n, rl, rr, 0, R ), extract ( 1, n, cl, cr, 1, C );
for ( auto r: R ) for ( auto c: C ) Dinic::add ( r.first, c.first, r.second * c.second );
} } // namespace SegmentTree. namespace Partition { int scnt, lef[MAXN + 5]; struct Segment {
int r1, r2, c; bool type;
inline bool operator < ( const Segment t ) const {
return c ^ t.c ? c < t.c : type < t.type;
}
} seg[MAXN * 2 + 5]; inline void readSeg () {
for ( int i = 1, r1, c1, r2, c2; i <= q; ++ i ) {
r1 = rint (), c1 = rint (), r2 = rint (), c2 = rint ();
seg[++ scnt] = { r1, r2, c1, 0 }, seg[++ scnt] = { r1, r2, c2, 1 };
}
seg[++ scnt] = { 1, n, n + 1, 0 }, seg[++ scnt] = { 1, n, n + 1, 1 };
} inline void part () {
std::sort ( seg + 1, seg + scnt + 1 );
for ( int i = 1; i <= scnt; ++ i ) {
if ( seg[i].type ) {
for ( int j = seg[i].r1; j <= seg[i].r2; ++ j ) {
lef[j] = seg[i].c;
}
} else {
for ( int j = seg[i].r1, lasw = seg[i].c - 1, lash = 0; j <= seg[i].r2 + 1; ++ j ) {
if ( j == seg[i].r2 + 1 || lef[j] ^ lasw ) {
if ( lasw + 1 < seg[i].c ) {
SegmentTree::secLink ( lash, j - 1, lasw + 1, seg[i].c - 1 );
}
lasw = lef[j], lash = j;
}
}
}
}
} } // namespace Partition. int main () {
n = rint (), q = rint ();
Partition::readSeg ();
SegmentTree::init ();
Partition::part ();
printf ( "%d\n", Dinic::calc () );
return 0;
}

Solution -「CF 793G」Oleg and Chess的更多相关文章

  1. Solution -「CF 1342E」Placing Rooks

    \(\mathcal{Description}\)   Link.   在一个 \(n\times n\) 的国际象棋棋盘上摆 \(n\) 个车,求满足: 所有格子都可以被攻击到. 恰好存在 \(k\ ...

  2. Solution -「CF 1622F」Quadratic Set

    \(\mathscr{Description}\)   Link.   求 \(S\subseteq\{1,2,\dots,n\}\),使得 \(\prod_{i\in S}i\) 是完全平方数,并最 ...

  3. Solution -「CF 923F」Public Service

    \(\mathscr{Description}\)   Link.   给定两棵含 \(n\) 个结点的树 \(T_1=(V_1,E_1),T_2=(V_2,E_2)\),求一个双射 \(\varph ...

  4. Solution -「CF 923E」Perpetual Subtraction

    \(\mathcal{Description}\)   Link.   有一个整数 \(x\in[0,n]\),初始时以 \(p_i\) 的概率取值 \(i\).进行 \(m\) 轮变换,每次均匀随机 ...

  5. Solution -「CF 1586F」Defender of Childhood Dreams

    \(\mathcal{Description}\)   Link.   定义有向图 \(G=(V,E)\),\(|V|=n\),\(\lang u,v\rang \in E \Leftrightarr ...

  6. Solution -「CF 1237E」Balanced Binary Search Trees

    \(\mathcal{Description}\)   Link.   定义棵点权为 \(1\sim n\) 的二叉搜索树 \(T\) 是 好树,当且仅当: 除去最深的所有叶子后,\(T\) 是满的: ...

  7. Solution -「CF 623E」Transforming Sequence

    题目 题意简述   link.   有一个 \(n\) 个元素的集合,你需要进行 \(m\) 次操作.每次操作选择集合的一个非空子集,要求该集合不是已选集合的并的子集.求操作的方案数,对 \(10^9 ...

  8. Solution -「CF 1023F」Mobile Phone Network

    \(\mathcal{Description}\)   Link.   有一个 \(n\) 个结点的图,并给定 \(m_1\) 条无向带权黑边,\(m_2\) 条无向无权白边.你需要为每条白边指定边权 ...

  9. Solution -「CF 599E」Sandy and Nuts

    \(\mathcal{Description}\)   Link.   指定一棵大小为 \(n\),以 \(1\) 为根的有根树的 \(m\) 对邻接关系与 \(q\) 组 \(\text{LCA}\ ...

随机推荐

  1. kali 2020.4 在安装typecho时,无法连接数据库的问题

    问题与环境 linux的环境为 kali 2020.4 php版本为:PHP 7.4.11 安装的typecho版本为:typechov1.0 遇到的问题是:在typecho初始化时,数据库的信息都填 ...

  2. R语言服务器程序 Rserve详解

    R语言服务器程序 Rserve详解 R的极客理想系列文章,涵盖了R的思想,使用,工具,创新等的一系列要点,以我个人的学习和体验去诠释R的强大. R语言作为统计学一门语言,一直在小众领域闪耀着光芒.直到 ...

  3. 以太 ip tcp udp 三次握手的理解

    以太帧: 1.前导码(7字节):使接收器建立比特同步. 2.起始定界符SFD(1字节):指示一帧的开始. 3.目的地址DA(6字节):指出要接收该帧的工作站. 4.源地址SA(6字节):指示发送该帧的 ...

  4. MVC框架---转

    浅析MVC模式 摘要:MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑.数据.界面 ...

  5. gin中XML/JSON/YAML/ProtoBuf 渲染

    package main import ( "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/testdata/p ...

  6. LNMP架构搭建

    目录 一:LNMP架构简介 1.Nginx与uwsgi 二:django框架+python 1.创建用户 2.安装依赖包 3.安装uwsgi和django 4.测试python 5.创建django项 ...

  7. python第三方模块详细教程(紧急情况宝典)

    目录 一:第三方模块 第三方模块:并不是python自带的 需要基于网络下载! 使用下载第三方模块 需要将python pip添加到环境变量 1.验证pip路径添加成功 2.pycharm添加第三方模 ...

  8. C语言读取写入CSV文件 [一]基础篇

    本系列文章目录 [一] 基础篇 [二] 进阶篇--写入CSV [三] 进阶篇--读取CSV 什么是CSV? CSV 是一种以纯文本形式存储的表格数据,具体介绍如下(来自维基百科): 逗号分隔值(Com ...

  9. ApacheCN 网络安全译文集 20211025 更新

    Android 渗透测试学习手册 中文版 第一章 Android 安全入门 第二章 准备实验环境 第三章 Android 应用的逆向和审计 第四章 对 Android 设备进行流量分析 第五章 And ...

  10. JavaCV的摄像头实战之五:推流

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 本文是<JavaCV的摄像头实战> ...