原文链接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;
}