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;
}

  

posted @ 2018-09-24 13:44  zzd233  阅读(585)  评论(0编辑  收藏  举报