如果不考虑C值的字典序最小的割的话,可以将先dp出以i结尾的LIS长度f[i],然后每个点拆点流量为Bi,对于
j<i,a[j]<a[i],f[j]+1=f[i]
j
<
i
,
a
[
j
]
<
a
[
i
]
,
f
[
j
]
+
1
=
f
[
i
]
,j拆出的出度点连向i的入度点,跑最小割就是最小花费
如果要求字典序最小的割,那么我们可以将C排序后枚举每条边看他能否在最小割中
判一条边(u,v)在不在某个割中,首先他必须满流,然后在残余网络中,只考虑还有流量的边,u和v必须不在同一个强联通中,因为若他们在同一个强联通中,因为反向弧(v,u)是有流量的,说明存在一条u到v的路径仍有流量,那(u,v)就没有割掉,相当于没割
这两个条件若都满足(u,v)可以是某个最小割中的边
我们已经取出了(u,v),考虑退掉他的影响,(u,v)对这个图的影响可以理解为(S,u)+(u,v)+(v,T),去掉(u,v),只需要跑(u,S),(T,v)两次最大流就可以把流退回来,因为删掉的是最小割中的边,退流后的残余网络仍是剩余图的最小割
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define inf 1e15
using namespace std;
inline void down(int &a,const int &b){if(a>b)a=b;}
const int maxn = 3100;
const int maxm = 6100000;
int n;
int A[maxn],B[maxn],C[maxn];
struct node{int i,c;}c[maxn];
inline bool cmp(const node x,const node y){return x.c<y.c;}
int f[maxn];
int S,T,st,ed;
int ei[maxn],use[maxm];
struct edge{int y,nex;ll c;}a[maxm]; int len,fir[maxn],fi[maxn];
inline void ins(const int x,const int y,const ll c)
{
a[++len]=(edge){y,fir[x],c};fir[x]=len;
a[++len]=(edge){x,fir[y],0};fir[y]=len;
}
int h[maxn];
queue<int>q;
bool bfs()
{
for(int i=1;i<=T;i++) h[i]=0;
h[st]=1; q.push(st);
while(!q.empty())
{
const int x=q.front(); q.pop();
for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y)
if(a[k].c&&!h[y]&&!use[k]) h[y]=h[x]+1,q.push(y);
}
return h[ed]>0;
}
ll dfs(const int x,const ll flow)
{
if(x==ed) return flow;
ll delta=0;
for(int &k=fi[x],y=a[k].y;k;k=a[k].nex,y=a[k].y)
{
if(a[k].c&&h[y]==h[x]+1&&!use[k])
{
ll cc=dfs(y,min(flow-delta,a[k].c));
a[k].c-=cc,a[k^1].c+=cc;
delta+=cc;
}
if(delta==flow) return delta;
}
return delta;
}
ll Flow(ll u)
{
ll re=0;
while(bfs())
{
for(int i=1;i<=T;i++) fi[i]=fir[i];
re+=dfs(st,u);
}
return re;
}
int dfn[maxn],low[maxn],id;
int t[maxn],tp; bool insta[maxn];
int bel[maxn],cnt;
void tarjan(const int x)
{
dfn[x]=low[x]=++id; insta[t[++tp]=x]=true;
for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(a[k].c&&!use[k])
{
if(!dfn[y]) tarjan(y),down(low[x],low[y]);
else if(insta[y]) down(low[x],dfn[y]);
}
if(low[x]==dfn[x])
{
++cnt; int la=0;
while(la!=x)
{
insta[la=t[tp--]]=false;
bel[la]=cnt;
}
}
}
int ans[maxn],ansn;
ll solve()
{
st=S,ed=T; ll re=Flow(LLONG_MAX);
ansn=0;
sort(c+1,c+n+1,cmp);
bool ok=false;
for(int i=1;i<=n;i++)
{
if(!ok)
{
cnt=id=0;
for(int j=1;j<=T;j++) dfn[j]=0;
for(int j=1;j<=T;j++) if(!dfn[j])
tarjan(j);
ok=true;
}
int ii=c[i].i;
int x=ii,y=ii+n;
if(bel[x]!=bel[y]&&!a[ei[ii]].c)
{
ok=false;
ans[++ansn]=ii;
use[ei[ii]]=1;
int u=a[ei[ii]^1].c;
st=x,ed=S; Flow(u);
st=T,ed=y; Flow(u);
}
}
return re;
}
int main()
{
int tcase; scanf("%d",&tcase);
while(tcase--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&A[i]);
for(int i=1;i<=n;i++) scanf("%d",&B[i]);
for(int i=1;i<=n;i++) scanf("%d",&C[i]);
f[0]=0; int mxf=0;
for(int i=1;i<=n;i++)
{
f[i]=0;
for(int j=0;j<i;j++) if(A[i]>A[j])
f[i]=max(f[i],f[j]+1);
mxf=max(mxf,f[i]);
}
S=2*n+1,T=S+1;
while(len) use[len--]=0;
len=1;for(int i=1;i<=T;i++) fir[i]=0;
for(int i=1;i<=n;i++)
{
ei[i]=len+1,ins(i,i+n,B[i]);
c[i].i=i,c[i].c=C[i];
if(f[i]==1) ins(S,i,inf);
if(f[i]==mxf) ins(i+n,T,inf);
for(int j=1;j<i;j++) if(f[j]+1==f[i]&&A[j]<A[i])
ins(j+n,i,inf);
}
printf("%lld ",solve());
sort(ans+1,ans+ansn+1);
printf("%d\n",ansn);
for(int i=1;i<ansn;i++) printf("%d ",ans[i]);
printf("%d\n",ans[ansn]);
}
return 0;
}