原文链接https://www.cnblogs.com/zhouzhendong/p/CF1045A.html

题目传送们 - CF1045A

题意

  你有 $n$ 个炮,有 $m$ 个敌人,敌人排成一列,编号从 $1$ 到 $m$ 。

  对于每门炮,可能是以下 3 种类型:

  1. 给定 $K$ ,以及一个包含 $K$ 个元素的集合,该炮最多集合内的一个敌人。保证对于所有这种类型的炮 $\sum K \leq 10^5$ 。

  2. 给定 $L,R$ ,该炮最多可以消灭区间 $[L,R]$ 中的一个敌人。

  3. 给定 $a,b,c$ ,该炮只能消灭集合 ${a,b,c}$ 中的 $0$ 或 $2$ 个敌人。保证所有这种类型的炮的敌人集合都不相交。

  每一个敌人最多被一个炮攻击。

  要求安排攻击方式,使得尽量多的敌人收到攻击。输出方案。

  $n,m\leq 10^5$

题解

  先扯个淡:

挑战手残新高度1: - 1s * 3600

挑战手残新高度2: - 1s * 2 * 3600

这个用图片描述不方便。口述吧!

哎呀这个网络流跑出来的结果怎么挂掉了???

while (1){ 建图……没问题;线段树……没问题}

都没问题啊??

然后我就黑人问号脸了。

忘记把和线段树同样范围的数组开成线段树范围了,虽然这个数组的取值并不影响我网络流跑出来的结果,但是炸了下标域,改变了我网络流需要涉及的数组取值。

白白被续 2h ,真******* 难受。

唉,我曾经天天splay LCT 时的码力已经不复存在了。

\kel 苟利国家生死以 \kel

  做法:

  首先我们忽略掉第 3 种炮,用暴力做:

  发现就是一个裸的二分图匹配。但是第二种炮的建边太多,还是不行。

  由于第二种炮是区间建边,我们就来一个线段树优化建图就好了。

  现在考虑第三种炮。如果第三种炮的攻击个数也可以是 $1$ ,那么就好做了。

  再仔细观察,题目里说到所有第三种炮的攻击集合互不相交。这个条件必然有用啊。怎么用?

  我们把第三种炮的攻击个数限制在 $[0,2]$ 之间,然后结合之前的建图方式,跑一次网络流来匹配一下。

  这样可能会导致有些 第三种炮 的攻击了 $1$ 个人。那么显然,在他的集合里,且没有攻击的人都被攻击了,而且都是被第一或第二种炮攻击的。

  于是我们手动让他再打掉一个被“第一或第二种炮攻击的”敌人,并让原先攻击这个敌人的炮不攻击。很显然,这样不仅攻击了最多的敌人,还满足了题目限定的条件,本题就可以完成了。

  细节处理比较复杂,详细参见代码。

  时间复杂度???O(网络流)

代码

#include <bits/stdc++.h>
using namespace std;
typedef int LL;
const int N=5005;
int read(){
int x=0,f=1;
char ch=getchar();
while (!isdigit(ch)&&ch!='-')
ch=getchar();
if (ch=='-')
f=-1,ch=getchar();
while (isdigit(ch))
x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return x*f;
}
struct Edge{
int x,y,nxt,cap;
Edge(){}
Edge(int a,int b,int c,int d){
x=a,y=b,cap=c,nxt=d;
}
};
struct Network{
static const int N=20005,M=1e6,INF=0x3FFFFFFF;
Edge e[M];
int n,S,T,fst[N],cur[N],cnt;
int q[N],dis[N],head,tail;
LL MaxFlow;
void clear(int _n){
n=_n,cnt=1;
memset(fst,0,sizeof fst);
}
void add(int a,int b,int c){
e[++cnt]=Edge(a,b,c,fst[a]),fst[a]=cnt;
e[++cnt]=Edge(b,a,0,fst[b]),fst[b]=cnt;
}
void init(){
for (int i=1;i<=n;i++)
cur[i]=fst[i];
}
void init(int _S,int _T){
S=_S,T=_T,MaxFlow=0,init();
}
int bfs(){
memset(dis,0,sizeof dis);
head=tail=0;
q[++tail]=T,dis[T]=1;
while (head<tail)
for (int x=q[++head],i=fst[x];i;i=e[i].nxt)
if (!dis[e[i].y]&&e[i^1].cap){
if (e[i].y==T)
return 1;
dis[q[++tail]=e[i].y]=dis[x]+1;
}
return (bool)dis[S];
}
int dfs(int x,int Flow){
if (x==T||!Flow)
return Flow;
int now=Flow;
for (int &i=cur[x];i;i=e[i].nxt){
int y=e[i].y;
if (dis[x]==dis[y]+1&&e[i].cap){
int d=dfs(y,min(now,e[i].cap));
e[i].cap-=d,e[i^1].cap+=d,now-=d;
if (now==0)
break;
}
}
return Flow-now;
}
LL Dinic(){
while (bfs())
init(),MaxFlow+=dfs(S,INF);
return MaxFlow;
}
LL Auto(int _S,int _T){
init(_S,_T);
return Dinic();
}
}g;
int n,m,tot,S,T;
int Type[N],ida[N],idsh[N],Turn[20005];
struct Node0{vector <int> id;Node1(){id.clear();}}a0[N];
struct Node1{vector <int> id;Node2(){id.clear();}}a1[N];
struct Node2{int a,b,c,ia,ib,ic;}a2[N];
struct Seg{int id,eL,eR;}t[N<<2];
void build(int rt,int L,int R){
Turn[t[rt].id=++tot]=rt;
if (L==R)
return (void)(g.add(t[rt].id,idsh[L],1e6),t[rt].eL=g.cnt);
int mid=(L+R)>>1,ls=rt<<1,rs=ls|1;
build(ls,L,mid);
build(rs,mid+1,R);
g.add(t[rt].id,t[ls].id,1e6),t[rt].eL=g.cnt;
g.add(t[rt].id,t[rs].id,1e6),t[rt].eR=g.cnt;
}
void update(int rt,int L,int R,int xL,int xR,int id,vector <int> &ie){
if (xR<L||R<xL)
return;
if (xL<=L&&R<=xR)
return g.add(id,t[rt].id,1),ie.push_back(g.cnt);
int mid=(L+R)>>1,ls=rt<<1,rs=ls|1;
update(ls,L,mid,xL,xR,id,ie);
update(rs,mid+1,R,xL,xR,id,ie);
}
int find(int rt){
if (!t[rt].eR)
if (g.e[t[rt].eL].cap)
return g.e[t[rt].eL].cap--,(g.e[t[rt].eL].x-T);
else
return 0;
if (g.e[t[rt].eL].cap)
if (int res=find(rt<<1))
return g.e[t[rt].eL].cap--,res;
if (g.e[t[rt].eR].cap)
if (int res=find(rt<<1|1))
return g.e[t[rt].eR].cap--,res;
return 0;
}
int Match[N],Mat[2][N];
vector <int> IDs[N<<2];
int Get0(int i){
for (vector <int> :: iterator p=a0[i].id.begin();p!=a0[i].id.end();p++)
if (g.e[*p].cap)
return g.e[*p].x-T;
return 0;
}
void Get1(int i){
for (vector <int> :: iterator p=a1[i].id.begin();p!=a1[i].id.end();p++)
if (g.e[*p].cap)
return IDs[Turn[g.e[*p].x]].push_back(i);
}
void Solve_Seg(int rt){
while (IDs[rt].size()>0)
Match[find(rt)]=IDs[rt].back(),IDs[rt].pop_back();
if (!t[rt].eR)
return;
Solve_Seg(rt<<1);
Solve_Seg(rt<<1|1);
}
int tag[N],cc[N];
int main(){
n=read(),m=read();
g.clear(30000);
tot=2,S=1,T=2;
for (int i=1;i<=m;i++)
g.add(idsh[i]=++tot,T,1);
build(1,1,m);
for (int i=1;i<=n;i++)
ida[i]=++tot;
for (int i=1;i<=n;i++){
Type[i]=read();
if (Type[i]==0){
int K=read();
g.add(S,ida[i],1);
for (int j=0;j<K;j++){
g.add(ida[i],idsh[read()],1);
a0[i].id.push_back(g.cnt);
}
}
if (Type[i]==1){
int L=read(),R=read();
g.add(S,ida[i],1);
update(1,1,m,L,R,ida[i],a1[i].id);
}
if (Type[i]==2){
g.add(S,ida[i],2);cc[i]=g.cnt;
g.add(ida[i],idsh[a2[i].a=read()],1),a2[i].ia=g.cnt;
g.add(ida[i],idsh[a2[i].b=read()],1),a2[i].ib=g.cnt;
g.add(ida[i],idsh[a2[i].c=read()],1),a2[i].ic=g.cnt;
}
}
g.n=tot;
int ans=g.Auto(S,T);
printf("%d\n",ans);
memset(Match,0,sizeof Match);
for (int i=1;i<=n;i++){
if (Type[i]==0) Match[Get0(i)]=i;
if (Type[i]==1) Get1(i);
}
Solve_Seg(1);
for (int i=1;i<=n;i++)
if (Type[i]==2){
int cnt=Mat[0][i]=Mat[1][i]=0;
if (g.e[a2[i].ia].cap)Mat[cnt++][i]=g.e[a2[i].ia].x-T;
if (g.e[a2[i].ib].cap)Mat[cnt++][i]=g.e[a2[i].ib].x-T;
if (g.e[a2[i].ic].cap)Mat[cnt++][i]=g.e[a2[i].ic].x-T;
if (cnt==1){
if (!g.e[a2[i].ia].cap)Match[a2[i].a]=0,Mat[cnt++][i]=a2[i].a;
else if (!g.e[a2[i].ib].cap)Match[a2[i].b]=0,Mat[cnt++][i]=a2[i].b;
else if (!g.e[a2[i].ic].cap)Match[a2[i].c]=0,Mat[cnt++][i]=a2[i].c;
}
Match[Mat[0][i]]=Match[Mat[1][i]]=i;
}
for (int i=1;i<=m;i++)
if (Match[i]!=0)
printf("%d %d\n",Match[i],i);
return 0;
}

  

Codeforces 1045A Last chance 网络流,线段树,线段树优化建图的更多相关文章

  1. Codeforces.1045A.Last chance(最大流ISAP 线段树优化建图)

    题目链接 \(Description\) 你需要用给定的\(n\)个武器摧毁\(m\)架飞船中的某一些.每架飞船需要被摧毁恰好一次. 武器共三种:1.可以在给定的集合中摧毁一架飞船:2.可以摧毁区间\ ...

  2. BZOJ 4276 [ONTAK2015]Bajtman i Okrągły Robin 费用流+线段树优化建图

    Description 有n个强盗,其中第i个强盗会在[a[i],a[i]+1],[a[i]+1,a[i]+2],...,[b[i]-1,b[i]]这么多段长度为1时间中选出一个时间进行抢劫,并计划抢 ...

  3. 【BZOJ4383】[POI2015]Pustynia 线段树优化建图

    [BZOJ4383][POI2015]Pustynia Description 给定一个长度为n的正整数序列a,每个数都在1到10^9范围内,告诉你其中s个数,并给出m条信息,每条信息包含三个数l,r ...

  4. AtCoder Regular Contest 069 F Flags 二分,2-sat,线段树优化建图

    AtCoder Regular Contest 069 F Flags 二分,2-sat,线段树优化建图 链接 AtCoder 大意 在数轴上放上n个点,点i可能的位置有\(x_i\)或者\(y_i\ ...

  5. loj#2255. 「SNOI2017」炸弹 线段树优化建图,拓扑,缩点

    loj#2255. 「SNOI2017」炸弹 线段树优化建图,拓扑,缩点 链接 loj 思路 用交错关系建出图来,发现可以直接缩点,拓扑统计. 完了吗,不,瓶颈在于边数太多了,线段树优化建图. 细节 ...

  6. bzoj3073: [Pa2011]Journeys 线段树优化建图

    bzoj3073: [Pa2011]Journeys 链接 BZOJ 思路 区间和区间连边.如何线段树优化建图. 和单点连区间类似的,我们新建一个点,区间->新点->区间. 又转化成了单点 ...

  7. BZOJ 3073: [Pa2011]Journeys Dijkstra+线段树优化建图

    复习一下线段树优化建图:1.两颗线段树的叶子节点的编号是公用的. 2.每次连边是要建两个虚拟节点 $p1,p2$ 并在 $p1,p2$ 之间连边. #include <bits/stdc++.h ...

  8. bzoj4383 [POI2015]Pustynia 拓扑排序+差分约束+线段树优化建图

    题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4383 题解 暴力的做法显然是把所有的条件拆分以后暴力建一条有向边表示小于关系. 因为不存在零环 ...

  9. codeforces 787D - Legacy 线段树优化建图,最短路

    题意: 有n个点,q个询问, 每次询问有一种操作. 操作1:u→[l,r](即u到l,l+1,l+2,...,r距离均为w)的距离为w: 操作2:[l,r]→u的距离为w 操作3:u到v的距离为w 最 ...

随机推荐

  1. CF 573B

    Bear and Blocks time limit per test 1 second memory limit per test 256 megabytes input standard inpu ...

  2. [PHP]flock文件IO锁的使用

    一.flock概述 bool flock  ( resource $handle  , int $operation  [, int &$wouldblock  ] ) 参数 handle 文 ...

  3. js 正则提取img标签的src值

    var r = '<img[^<>]*? src="([^<>]*?)"'; var yyy=content.match(r); alert(yyy[ ...

  4. grep,find

    grep是强大的文本搜索工具,他可以对文件逐行查看,如果找到匹配的模式,就可以打印出包含次模式的所有行,并且支持正则表达式 find查找文件的grep是来查找字符串的,文件的内容 grep 文件的内容 ...

  5. Codeforces 446C DZY Loves Fibonacci Numbers [线段树,数论]

    洛谷 Codeforces 思路 这题知道结论就是水题,不知道就是神仙题-- 斐波那契数有这样一个性质:\(f_{n+m}=f_{n+1}f_m+f_{n}f_{m-1}\). 至于怎么证明嘛-- 即 ...

  6. 修改html中button显示的文字

    1. <input type="button">  实现密码输入框的可见和隐藏 直接修改value属性即可 <script type="text/jav ...

  7. Modbus库开发笔记之二:Modbus消息帧的生成

    前面我们已经对Modbus的基本事务作了说明,也据此设计了我们将要实现的主从站的操作流程.这其中与Modbus直接相关的就是Modbus消息帧的生成.Modbus消息帧也是实现Modbus通讯协议的根 ...

  8. Centos7 linux 卸载自带安装的jdk 并yum自动安装jdk1.8

    一:卸载系统自带安装的JDK 注:本文参考了< 使用CentOS7卸载自带jdk安装自己的JDK1.8> 通过xshell工具成功连接安装好的虚拟机之后可通过    rpm -qa | g ...

  9. pythonz之__new__与__init__

    new __new__是用来控制对象的生成过程,在对象生成之前 __init__是用来完善对象的 如果new方法不返回对象(return super().new(cls)),则不会调用init函数 c ...

  10. JPA核心类与使用

    点击访问:JPA环境配置(一) Persistence: Persistence用于获取EntityManagerFactory实例,这个类中包含一个名为createEntityManagerFact ...