【题目描述】:
给定序列A,序列中的每一项Ai有删除代价Bi和附加属性Ci。请删除若干项,使得4的最长上升子序列长度减少至少1,且付出的代价之和最小,并输出方案。
如果有多种方案,请输出将删去项的附加属性排序之后,字典序最小的一种。
【题目解法】:
首先第一问是裸的最小割
我们用f[i]表示以i结尾的最长上升子序列长度
然后将每个点i拆成i和i'
对于f[i]=1的点,连边(s,i,INF)
对于f[i]=MaxF点的,连边(i',t,INF)
然后对于每个点i,连边(i,i',b[i])
对于两个点i,j满足a[j]<a[i]且f[j]+1=f[i],连边(j',i,INF)
最小割就是答案,这样就有30pt
考虑构造字典序最小的方案,我们按照C排序,然后贪心的加入
考虑什么情况下某条边(i,i')一定在某个最小割中?
很简单,两个条件:
1.(i,i')满流(显然)
2.我们强制割开i i',如果最小割不变,那么(i,i')一定在某个最小割中
如何强制割开某条边呢?我们连边(s,i,INF) (i',t,INF),这样就可以强制将i留在s割,i'留在t割,自然这条边就割开了
但是这样判断每次都要跑最大流,会TLE...
我们考虑如果i->i'之间如果存在增广路,那么一定有s->i->i'->t的增广路,此时最小割会增大
如果i->i'之间没有增广路,那么最小割不会增大。
因此我们O(N)判断一下i->i'是否存在增广路即可
然后考虑删去(i,i')对图的影响,我们显然需要把(i,i')对原图的影响恢复回去
也就是说把流过(i,i')的流退回去即可
那么我们Sap(t,i') Sap(i,s),然后把(i,i')删去即可
然后TLE辣TAT
不虚,我会卡常数...
QwQ...慢慢都是泪啊
我们考虑推流是一定退了b[i],因此Sap时判断最大流是否已经等于b[i]了
然后就过了TAT
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N=2005;
const int M=2000005;
const int INF=32083208;
struct edge {int x,y,op,next; bool flag;} b[M];
int cases,n,S,T,id[N][2],A[N],B[N],C[N],f[N],DP;
int tot,a[N],Id[N],maxflow,ans,Ans[N],pos[N],aim;
inline long long getint()
{
long long x=0; char c=getchar(); bool flag=false;
while ((c!='-')&&((c<'0')||(c>'9'))) c=getchar();
if (c=='-') flag=true,c=getchar();
while ((c>='0')&&(c<='9')) x=x*10+(long long)(c-'0'),c=getchar();
if (flag) return -x; else return x;
}
inline void addedge(int x,int y,int z)
{
++tot; b[tot].x=y; b[tot].y=z; b[tot].op=tot+1; b[tot].flag=false; b[tot].next=a[x]; a[x]=tot;
++tot; b[tot].x=x; b[tot].y=0; b[tot].op=tot-1; b[tot].flag=false; b[tot].next=a[y]; a[y]=tot;
}
void init()
{
n=getint(); f[0]=0; DP=0; ans=0; aim=INF;
for (int i=1; i<=n; i++) A[i]=getint();
for (int i=1; i<=n; i++) B[i]=getint();
for (int i=1; i<=n; i++) C[i]=getint();
for (int i=1; i<=n; i++) f[i]=1;
for (int i=1; i<=n; i++) for (int j=1; j<i; j++) if (A[j]<A[i]) f[i]=max(f[i],f[j]+1);
for (int i=1; i<=n; i++) DP=max(DP,f[i]);
}
void build()
{
S=0; T=0; tot=0; memset(a,0,sizeof(a));
for (int i=1; i<=n; i++) id[i][0]=++T,id[i][1]=++T; ++T;
for (int i=1; i<=n; i++) addedge(id[i][0],id[i][1],B[i]),Id[i]=tot-1;
for (int i=1; i<=n; i++) if (f[i]==1) addedge(S,id[i][0],INF);
for (int i=1; i<=n; i++) if (f[i]==DP) addedge(id[i][1],T,INF);
for (int i=1; i<=n; i++) for (int j=1; j<i; j++) if ((A[j]<A[i])&&(f[j]+1==f[i])) addedge(id[j][1],id[i][0],INF);
}
namespace NetWork
{
int pre[N],gap[N],cur[N],dis[N],now;
inline void augment(int s,int t)
{
int flow=INF;
for (int i=s; i!=t; i=b[cur[i]].x) flow=min(flow,b[cur[i]].y);
for (int i=s; i!=t; i=b[cur[i]].x) b[cur[i]].y-=flow,b[b[cur[i]].op].y+=flow;
maxflow+=flow; now=s;
}
void Sap(int s,int t)
{
for (int i=S; i<=T; i++) cur[i]=a[i],gap[i]=0,pre[i]=0,dis[i]=0;
now=s; gap[t]=T; maxflow=0;
while (dis[s]<=T)
{
if (maxflow==aim) return;
if (now==t) augment(s,t); bool Flag=false;
for (int p=cur[now];p;p=b[p].next)
{
int pp=b[p].x; if ((b[p].y<=0)||(dis[pp]+1!=dis[now])) continue;
pre[pp]=now; cur[now]=p; Flag=true; now=pp; break;
}
if (Flag) continue; cur[now]=a[now];
gap[dis[now]]--; if (gap[dis[now]]==0) break; dis[now]=T+1;
for (int p=cur[now];p;p=b[p].next)
{
int pp=b[p].x;
if (b[p].y>0) dis[now]=min(dis[now],dis[pp]+1);
}
gap[dis[now]]++; if (now!=s) now=pre[now];
}
}
}
namespace GetAns
{
int head,tail,q[N],times; int v[N];
inline bool cmp(int x,int y) {return C[x]<C[y];}
/*bool bfs(int s,int t)
{
memset(v,false,sizeof(v));
v[s]=true; head=0; tail=1; q[1]=s;
while (head<tail)
{
int k=q[++head];
for (int p=a[k];p;p=b[p].next)
{
int pp=b[p].x; if (b[p].y<=0) continue;
if (pp==t) return true; if (!v[pp]) v[q[++tail]=pp]=true;
}
}
return false;
}*/
bool dfs(int s,int t)
{
if (v[s]==times) return false; if (s==t) return true; v[s]=times;
for (int p=a[s];p;p=b[p].next) if ((b[p].y>0)&&(dfs(b[p].x,t))) return true;
return false;
}
void solve()
{
memset(v,0,sizeof(v));
for (int i=1; i<=n; i++) pos[i]=i;
int flow=0,Maxflow=maxflow;
sort(pos+1,pos+n+1,cmp); printf("%d ",maxflow);
for (int i=1; i<=n; i++)
{
int now=pos[i],x=id[now][0],y=id[now][1];
if (b[Id[now]].y!=0) continue; ++times; if (dfs(x,y)) continue;
Ans[++ans]=now; flow+=B[now]; if (flow==Maxflow) break;
aim=B[now]; NetWork::Sap(T,y); NetWork::Sap(x,S);
b[Id[now]].y=0; b[b[Id[now]].op].y=0;
}
sort(Ans+1,Ans+ans+1); printf("%d\n",ans);
for (int i=1; i<=ans-1; i++) printf("%d ",Ans[i]); printf("%d\n",Ans[ans]);
}
}
int main()
{
cases=getint();
while (cases--)
{
init();
build();
NetWork::Sap(S,T);
GetAns::solve();
}
return 0;
}