2018.09.27 codeforces1045A. Last chance(线段树优化建图+最大流)

传送门
看完题应该都知道是网络流了吧。
但是第二种武器直接建图会gg。
因此我们用线段树优化建图。
具体操作就是,对于这m个人先建一棵线段树,父亲向儿子连容量为inf的边,最后叶子结点向对应的人连容量为1的边。
这样给第二种武器对应连边的时候直接给区间连边就行了。
对于操作三,我们直接贪心流掉两个人,剩下的一个人不流就行了。
代码:

#include
#define N 200005
#define inf 0x3f3f3f3f 
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (l+r>>1)
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
bool vis[N];
int n,m,id[N],q[N],hd,tl,tot,Begin,ans,d[N],first[N],cnt,s,t;
struct edge{int v,c,next;};
map<int,int>mp[N];
edge e[N];
inline void add(int u,int v,int c){
	e[++cnt].v=v,e[cnt].c=c,e[cnt].next=first[u],first[u]=cnt;
	e[++cnt].v=u,e[cnt].c=0,e[cnt].next=first[v],first[v]=cnt;
}
inline bool bfs(){
	queue<int>q;
	q.push(s);
	memset(d,-1,sizeof(d));
	d[s]=0;
	while(!q.empty()){
		int x=q.front();
		q.pop();
		for(int i=first[x];i!=-1;i=e[i].next){
			int v=e[i].v;
			if(e[i].c<=0||d[v]!=-1)continue;
			d[v]=d[x]+1;
			if(v==t)return true;
			q.push(v);
		}
	}
	return false;
}
inline int dfs(int x,int f){
	if(!f||x==t)return f;
	int flow=f;
	for(int i=first[x];i!=-1;i=e[i].next){
		int v=e[i].v;
		if(flow&&e[i].c>0&&d[v]==d[x]+1){
			int tmp=dfs(v,min(flow,e[i].c));
			if(tmp==0)d[v]=-1;
			flow-=tmp;
			e[i].c-=tmp;
			e[i^1].c+=tmp;
		}
	}
	return f-flow;
}
inline void solve(){while(bfs())ans+=dfs(s,inf);}
inline void build(int p,int l,int r){
	id[p]=++tot;
	if(l==r){add(id[p],l,1);return;}
	build(lc,l,mid),build(rc,mid+1,r);
	add(id[p],id[lc],inf),add(id[p],id[rc],inf);
}
inline void update(int p,int l,int r,int ql,int qr){
	if(ql<=l&&r<=qr){add(tot,id[p],1);return;}
	if(qr<=mid)update(lc,l,mid,ql,qr);
	else if(ql>mid)update(rc,mid+1,r,ql,qr);
	else update(lc,l,mid,ql,mid),update(rc,mid+1,r,mid+1,qr);
}
int tmp=0;
inline void query(int p){
	if(p>=Begin){tmp=p-Begin+1;return;}
	map<int,int>::iterator it=mp[p].begin();
	query(it->first);
	--it->second;
	if(!it->second)mp[p].erase(it);
}
int main(){
	memset(first,-1,sizeof(first)),n=read(),tot=m=read(),cnt=-1,s=++tot,build(1,1,m),Begin=tot+1;
	for(int i=1;i<=n;++i){
		int op=read();
		++tot;
		if(op==0){
			int k=read();
			while(k--){
				int x=read();
				add(tot,x,1);
			}
			add(s,tot,1);
		}
		else if(op==1){
			int ql=read(),qr=read();
			update(1,1,m,ql,qr),add(s,tot,1);
		}
		else{
			int a=read(),b=read(),c=read();
			vis[a]=vis[b]=1,ans+=2;
			add(tot,a,0),e[cnt].c=1,add(tot,b,0),e[cnt].c=1,add(tot,c,1);
		}
	}
	t=++tot;
	for(int i=1;i<=m;++i)if(!vis[i])add(i,t,1);
	solve();
	printf("%d\n",ans);
	for(int i=1;i<=tot;++i)for(int j=first[i];j;j=e[j].next)if((j&1)&&e[j].c)mp[i][e[j].v]=e[j].c; 
	for(int i=1;i<=m;++i){
		bool f=true;
		for(int j=first[i];j&&f;j=e[j].next)if(e[j].v==t&&e[j].c)f=false;
		if(f)query(i),printf("%d %d\n",tmp,i);
	}
	return 0;
}

你可能感兴趣的:(#,线段树,#,最大流)