Codeforces 1045A Last chance 网络流,线段树,线段树优化建图
原文链接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 网络流,线段树,线段树优化建图的更多相关文章
- Codeforces.1045A.Last chance(最大流ISAP 线段树优化建图)
题目链接 \(Description\) 你需要用给定的\(n\)个武器摧毁\(m\)架飞船中的某一些.每架飞船需要被摧毁恰好一次. 武器共三种:1.可以在给定的集合中摧毁一架飞船: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时间中选出一个时间进行抢劫,并计划抢 ...
- 【BZOJ4383】[POI2015]Pustynia 线段树优化建图
[BZOJ4383][POI2015]Pustynia Description 给定一个长度为n的正整数序列a,每个数都在1到10^9范围内,告诉你其中s个数,并给出m条信息,每条信息包含三个数l,r ...
- AtCoder Regular Contest 069 F Flags 二分,2-sat,线段树优化建图
AtCoder Regular Contest 069 F Flags 二分,2-sat,线段树优化建图 链接 AtCoder 大意 在数轴上放上n个点,点i可能的位置有\(x_i\)或者\(y_i\ ...
- loj#2255. 「SNOI2017」炸弹 线段树优化建图,拓扑,缩点
loj#2255. 「SNOI2017」炸弹 线段树优化建图,拓扑,缩点 链接 loj 思路 用交错关系建出图来,发现可以直接缩点,拓扑统计. 完了吗,不,瓶颈在于边数太多了,线段树优化建图. 细节 ...
- bzoj3073: [Pa2011]Journeys 线段树优化建图
bzoj3073: [Pa2011]Journeys 链接 BZOJ 思路 区间和区间连边.如何线段树优化建图. 和单点连区间类似的,我们新建一个点,区间->新点->区间. 又转化成了单点 ...
- BZOJ 3073: [Pa2011]Journeys Dijkstra+线段树优化建图
复习一下线段树优化建图:1.两颗线段树的叶子节点的编号是公用的. 2.每次连边是要建两个虚拟节点 $p1,p2$ 并在 $p1,p2$ 之间连边. #include <bits/stdc++.h ...
- bzoj4383 [POI2015]Pustynia 拓扑排序+差分约束+线段树优化建图
题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4383 题解 暴力的做法显然是把所有的条件拆分以后暴力建一条有向边表示小于关系. 因为不存在零环 ...
- 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 最 ...
随机推荐
- Struts2配置拦截器
<package name="loginaction" namespace="/" extends="struts-default"& ...
- php学习随笔--定时触发
PHP访问接口方法:
- git reset --hard xxxxxxx
关于git reset --hard xxxxxxx命令之Git版本回退 今晚代码写着写着就头脑有点发懵,手指也不听使唤了竟然让我敲出了 git reset --hard 命令,然后的然后就是之前所有 ...
- Confluence 6 的系统配置信息的示例
awt.toolkit sun.awt.X11.XToolkit file.encoding.pkg sun.io java.specification.version 1.8 sun.cpu.isa ...
- Confluence 6 数据库表-杂项(Miscellaneous)
这些部分是一些其他的表格,这些表格有必要在这里提及下能帮你更好的了解系统. os_propertyentry 有关实体和属性相关的特性. bandana 所有的持久层.这个表格包含的的内容有用户设置和 ...
- 自执行匿名函数: (function() { /* code */ })();
1,常见格式:(function() { /* code */ })(); 2,解释:包围函数(function(){})的第一对括号向脚本返回未命名的函数,随后一对空括号立即执行返回的未命名函数,括 ...
- 第十六单元 yum管理RPM包
yum的功能 本地yum配置 光盘挂载和镜像挂载 本地yum配置 网络yum配置 网络yum配置 Yum命令的使用 使用yum安装软件 使用yum删除软件 安装组件 删除组件 清除缓存 查询 课后作业 ...
- Niagara 泵阀
1.泵阀系统 2.Niagara 每次在启动workbench的时候安装一下daemon,后启动platform 启动station的时候,当station的status变成Running之后启动
- Tmk吃汤饭
问题 : Tmk吃汤饭 时间限制: 1 Sec 内存限制: 128 MB 题目描述 Tmk喜欢吃汤饭! Tmk喜欢吃汤饭!! Tmk喜欢吃汤饭!!! 汤饭窗口分点餐和取餐两个窗口,一位蜀黍负责点餐窗 ...
- eclipse检出SVN项目的正确步骤
一.在工作空间新建工作目录:workspace-xf 二.在工作目录下workspace-xf 新建文件夹 tdvs ,进入该文件夹鼠标右键:SVN CheckOut 检出需要的项目 三.打开ecl ...