bzoj 3532: [Sdoi2014]Lis

Description

给定序列A,序列中的每一项Ai有删除代价Bi和附加属性Ci。请删除若
干项,使得4的最长上升子序列长度减少至少1,且付出的代价之和最小,并输出方案。
如果有多种方案,请输出将删去项的附加属性排序之后,字典序最小的一种。

Solution

首先可以建图求出最小的代价和 .
由于是删除点 , 所以我们可以要拆点 .
于是分成两个集合 \(i,i+n\) .
如果 \(f[i]=1\) , \(S->i\)
如果 \(f[i]=maxf\) \(i+n->T\)
如果 \(f[i]=f[j]+1\&\&a[j]<a[i]\) , \(i+n->i\)
最小割就是答案 .
然后考虑构造字典序最小的解 , 首先按 \(C_i\) 排序 , 从小到大考虑 .
一条边能够加入最小割显然要加入 , 我们就需要判断这条边是否存在最小割集中就行了 .
一种方法是减小 \(i->i+n\) 这条边 \(1\) 的流量 , 如果最小割也减少 \(1\) 那么就可以成为最小割的一条边
也可以通过判断是否存在 \(i->i+n\) 的增广路 , 如果存在则不会在最小割集中 .
这样的话我们删除一条边 \((u,v,c)\) 后要消除它的影响 , 为了节省复杂度 .
\(maxflow(T,v,c),maxflow(u,S,c)\) 再把这条边容量清零 , 最后再跑一边 \(maxflow(S,T,inf)\) 就好了.
但是这个题的图有特殊性质 , 所以只需要 \(maxflow(T,v),maxflow(u,S)\) 就好了.
注意常数优化 :
比如 \(bfs\) 的时候要在遍历到 \(T\) 时就 $return $ .

#include<bits/stdc++.h>
using namespace std;
template<class T>void gi(T &x){
	int f;char c;
	for(f=1,c=getchar();c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
	for(x=0;c<='9'&&c>='0';c=getchar())x=x*10+(c&15);x*=f;
}
const int N=1550,inf=2e9;
int n,c[N],id[N],a[N],b[N],f[N];
int dis[N*N],S=0,T,head[N],nxt[N*N],to[N*N],num=1,dep[N],ans[N];
inline void link(int x,int y,int z){
	nxt[++num]=head[x];to[num]=y;head[x]=num;dis[num]=z;
	nxt[++num]=head[y];to[num]=x;head[y]=num;dis[num]=0;
}
inline bool comp(const int &i,const int &j){return c[i]<c[j];}
inline bool bfs(int s,int t){
	queue<int>Q;
	for(int i=2*n+1;i>=0;i--)dep[i]=0;
	Q.push(s);dep[s]=1;
	while(!Q.empty()){
		int x=Q.front();Q.pop();
		for(int i=head[x];i;i=nxt[i]){
			if(dis[i]<=0 || dep[to[i]])continue;
			int u=to[i];
			dep[u]=dep[x]+1;Q.push(u);
			if(u==t)return 1;
		}
	}
	return dep[t];
}
inline int dfs(int x,int flow){
	if(x==T || !flow)return flow;
	int tot=0,t;
	for(int i=head[x];i;i=nxt[i]){
		int u=to[i];
		if(dis[i]<=0 || dep[u]!=dep[x]+1)continue;
		t=dfs(u,min(flow,dis[i]));
		dis[i]-=t;dis[i^1]+=t;tot+=t;flow-=t;
		if(!flow)break;
	}
	if(!tot)dep[x]=-1;
	return tot;
}
inline int Dinic(int s,int t){
	S=s;T=t;
	int tot=0,x;
	while(bfs(s,t)){
		x=dfs(s,inf);
		while(x)tot+=x,x=dfs(s,inf);
	}
	return tot;
}
inline void work(){
	int cnt=0;
	memset(head,0,sizeof(head));num=1;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)gi(a[i]);
	for(int i=1;i<=n;i++)gi(b[i]);
	for(int i=1;i<=n;i++)gi(c[i]),id[i]=i;
	sort(id+1,id+n+1,comp);
	int maxf=1;
	for(int i=1;i<=n;i++){
		f[i]=1;
		for(int j=1;j<i;j++)if(a[j]<a[i])f[i]=max(f[i],f[j]+1);
		maxf=max(maxf,f[i]);
	}
	for(int i=1;i<=n;i++){
		if(f[i]==1)link(0,i,inf);
	   if(f[i]==maxf)link(i+n,2*n+1,inf);
		for(int j=1;j<i;j++)
			if(f[j]+1==f[i] && a[j]<a[i])link(j+n,i,inf);
		link(i,i+n,b[i]);
	}
	int sum=Dinic(0,2*n+1);
	for(int i=1;i<=n;i++){
		int u=id[i],v=id[i]+n;
		if(bfs(u,v))continue;
		Dinic(2*n+1,v);Dinic(u,0);
		ans[++cnt]=u;
	}
	sort(ans+1,ans+cnt+1);
	printf("%d %d\n",sum,cnt);
	printf("%d",ans[1]);
	for(int i=2;i<=cnt;i++)printf(" %d",ans[i]);
}
int main(){
  freopen("pp.in","r",stdin);
  freopen("pp.out","w",stdout);
  int T;cin>>T;
  while(T--)work(),T?puts(""):0;
  return 0;
}

posted @ 2018-08-04 19:41  PIPIBoss  阅读(61)  评论(0编辑  收藏