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