原文链接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. HDU 5033

    题意: 给你 N 楼房, 然后给你m个人站在这些楼房之间, 问看到天空的仰角是多少度 思路: 对于每一个人, 算出左边的凸包, 和右边的凸包, 找出最大斜率点, 算角度即可 (我在线算比较费时, 离线 ...

  2. js -- sort() 使用排序函数

    JavaScript sort() 方法 JavaScript Array 对象 定义和用法 sort() 方法用于对数组的元素进行排序. 语法 arrayObject.sort(sortby) 参数 ...

  3. Webapi 跨域 解决解决错误No 'Access-Control-Allow-Origin' header is present on the requested resource 问题

    首先是web端(http://localhost:53784) 请求 api(http://localhost:81/api/)时出现错误信息: 查看控制台会发现错误:XMLHttpRequest c ...

  4. bzoj3730 震波 [动态点分治,树状数组]

    传送门 思路 如果没有强制在线的话可以离线之后CDQ分治随便搞. 有了强制在线之后--可能可以二维线段树?然而我不会算空间. 然后我们莫名其妙地想到了动态点分治,然后这题就差不多做完了. 点分树有一个 ...

  5. jemter 新增sha256函数

    1  https://blog.csdn.net/y100100/article/details/80701049 2  https://blog.csdn.net/y100100/article/d ...

  6. Oauth2.0 QQ&微信&微博实现第三方登陆

    一.写在前面 目前对于大多数的App或Web网站都支持有第三方登陆这个功能,用户可使用 QQ/ 微信/ 微博 帐号快速登录你的网站,降低注册门槛,为你的网站带来海量新用户.最近在新项目上刚好用到了,在 ...

  7. Oracle数据库内存使用情况分析查看

    SGA.PGA使用情况 select name,total,round(total-free,2) used, round(free,2) free,round((total-free)/total* ...

  8. vuejs中使用echarts

    <style scoped> .content { /*自行添加样式即可*/ } #main { /*需要制定具体高度,以px为单位*/ height: 400px; } </sty ...

  9. ES6之Set与Map加深理解

    Set 类似于数组,但是成员的值都是唯一的,没有重复的值,有序. Set函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化. 用途 数组去重: [...new ...

  10. javaScript遍历对象、数组总结

        javaScript遍历对象总结 1.使用Object.keys()遍历 返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性). var obj = {'0':'a ...