【BZOJ-3532】Lis 最小割 + 退流
3532: [Sdoi2014]Lis
Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 704 Solved: 264
[Submit][Status][Discuss]
Description
给定序列A,序列中的每一项Ai有删除代价Bi和附加属性Ci。请删除若干项,使得4的最长上升子序列长度减少至少1,且付出的代价之和最小,并输出方案。
如果有多种方案,请输出将删去项的附加属性排序之后,字典序最小的一种。
Input
输入包含多组数据。
输入的第一行包含整数T,表示数据组数。接下来4*T行描述每组数据。
每组数据的第一行包含一个整数N,表示A的项数,接下来三行,每行N个整数A1..An,B1.,Bn,C1..Cn,满足1 < =Ai,Bi,Ci < =10^9,且Ci两两不同。
Output
对每组数据,输出两行。第一行包含两个整数S,M,依次表示删去项的代价和与数量;接下来一行M个整数,表示删去项在4中的的位置,按升序输出。
Sample Input
1
6
3 4 4 2 2 3
2 1 1 1 1 2
6 5 4 3 2 1
6
3 4 4 2 2 3
2 1 1 1 1 2
6 5 4 3 2 1
Sample Output
4 3
2 3 6
解释:删去(A2,43,A6),(A1,A6),(A2,43,44,A5)等都是合法的方案,但{A2,43,A6)对应的C值的字典序最小。
2 3 6
解释:删去(A2,43,A6),(A1,A6),(A2,43,44,A5)等都是合法的方案,但{A2,43,A6)对应的C值的字典序最小。
HINT
1 < =N < =700 T < =5
Source
Solution
最小割的模型还是很显然的,先dp一边,然后转移之间连边,源点到初始状态,最终状态到汇点,拆点限制流量,最小割即删去的代价。
然后考虑字典序最小的割,可以通过按字典序枚举每条限流边,如果这条边满流且无法再流过去,则这条割边可选,然后消除它的影响,继续处理。
消除影响可以利用退流的思想,其实就是将这条边<u,v>正反置0,然后跑一遍T->v,一遍u->S即可。
Code
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<queue> using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();} while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();} return x*f; } #define MAXN 5010 int N,cas,a[MAXN],b[MAXN],c[MAXN]; struct EdgeNode{ int next,to,cap; }edge[2000100]; int head[MAXN],cnt=1; inline void AddEdge(int u,int v,int w) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v; edge[cnt].cap=w;} inline void InsertEdge(int u,int v,int w) {AddEdge(u,v,w); AddEdge(v,u,0);} int h[MAXN],cur[MAXN],S,T; queue<int>q; inline bool bfs(int s,int t) { for (int i=S; i<=T; i++) h[i]=-1; q.push(s); h[s]=0; while (!q.empty()) { int now=q.front(); q.pop(); for (int i=head[now]; i; i=edge[i].next) if (edge[i].cap && h[edge[i].to]==-1) h[edge[i].to]=h[now]+1,q.push(edge[i].to); } return h[t]!=-1; } inline int dfs(int now,int low,int s,int t) { if (now==t) return low; int w,used=0; for (int i=cur[now]; i; i=edge[i].next) if (edge[i].cap && h[edge[i].to]==h[now]+1) { int w=dfs(edge[i].to,min(low-used,edge[i].cap),s,t); edge[i].cap-=w; edge[i^1].cap+=w; used+=w; if (used==low) return used; if (edge[i].cap) cur[now]=i; } if (!used) h[now]=-1; return used; } #define INF 0x7fffffff inline int Dinic(int s,int t) { int re=0; while (bfs(s,t)) { for (int i=S; i<=T; i++) cur[i]=head[i]; re+=dfs(s,INF,s,t); } return re; } #define Pa pair<int,int> #define MP make_pair #define Id first #define ps second inline bool cmp(Pa x,Pa y) {return c[x.Id]<c[y.Id];} int stack[MAXN],top=0; int main() { cas=read(); while (cas--) { cnt=1; memset(head,0,sizeof(head)); N=read(); for (int i=1; i<=N; i++) a[i]=read(); for (int i=1; i<=N; i++) b[i]=read(); for (int i=1; i<=N; i++) c[i]=read(); static int f[MAXN]; for (int i=1; i<=N; i++) f[i]=1; for (int i=1; i<=N; i++) for (int j=0; j<i; j++) if (a[i]>a[j]) f[i]=max(f[i],f[j]+1); int mx=0; for (int i=1; i<=N; i++) mx=max(mx,f[i]); S=0,T=2*N+1; static Pa id[MAXN]; for (int i=1; i<=N; i++) { if (f[i]==1) InsertEdge(S,i,INF); if (f[i]==mx) InsertEdge(i+N,T,INF); InsertEdge(i,i+N,b[i]); id[i]=MP(i,cnt); for (int j=1; j<i; j++) if (f[i]-1==f[j] && a[i]>a[j]) InsertEdge(j+N,i,INF); } int ans=Dinic(S,T); stable_sort(id+1,id+N+1,cmp); top=0; for (int i=1; i<=N; i++) { int e1=id[i].ps^1,e2=id[i].ps,p1=id[i].Id,p2=p1+N; if (!edge[e1].cap && !bfs(p1,p2)) { Dinic(p1,S); Dinic(T,p2); edge[e1].cap=edge[e2].cap=0; stack[++top]=id[i].Id; } } stable_sort(stack+1,stack+top+1); printf("%d %d\n",ans,top); for (int i=1; i<top; i++) printf("%d ",stack[i]); printf("%d\n",stack[top]); } return 0; }
——It's a lonely path. Don't make it any lonelier than it has to be.