SDOI2014 LIS
题目链接:戳我
我们先做一个DP,就能求出来到前i位的最长上升子序列的长度(maxx[i]数组)。
然后我们考虑求最小割——给每个点拆点,如果要割掉这个点,就相当于把in[i]--out[i]这条边给割掉了。
然后如果在最长上升子序列中,该位下面可以接很多,那么就从该位代表的out向下面可以接的in连边。
然后跑最小割就行了QAQ
但是......之后还需要求字典序最小的最小割方案。
那么我们先把边从小到大排个序,然后把所有可能割边都捞出来,贪心地选择。
但是有一个问题,就是我们选择了一个边之后,它会对我们之后的决策产生影响(比如说,本来应该选的边可以不选了)
所以我们要强行退流(具体操作可以看代码)。我们知道选了这条边之后,这条边的可替代品就不需要选了,这些可替代它的边显然是存在于S-u,v-T之间的。所以我们直接退掉他们满流边的流量就行啦!
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define S 0
#define T 2*n+1
#define MAXN 1510
#define INF 0x3f3f3f3f
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch==-1)f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int TT,n,t=1,cnt,kkk;
int num[MAXN];
int ans[MAXN],in[MAXN],out[MAXN],done[MAXN];
int head[MAXN],cur[MAXN],dis[MAXN],dp[MAXN][MAXN],maxx[MAXN];
struct Node{int id,a,b,c;}node[MAXN];
struct Edge{int nxt,to,dis;}edge[MAXN*MAXN*2];
inline void add(int from,int to,int dis)
{
// printf("[%d %d] %d\n",from,to,dis);
edge[++t].nxt=head[from],edge[t].to=to,edge[t].dis=dis,head[from]=t;
edge[++t].nxt=head[to],edge[t].to=from,edge[t].dis=0,head[to]=t;
}
inline bool check(int s,int t)
{
queue<int>q;
memset(done,0,sizeof(done));
q.push(s);done[s]=1;
while(!q.empty())
{
int u=q.front();q.pop();
if(u==t) return true;
for(int i=head[u];i;i=edge[i].nxt)
{
int v=edge[i].to;
if(edge[i].dis&&done[v]==0)
q.push(v),done[v]=1;
}
}
return false;
}
inline bool bfs(int s,int t)
{
queue<int>q;
memset(dis,0x3f,sizeof(dis));
memcpy(cur,head,sizeof(head));
q.push(s);dis[s]=0;
while(!q.empty())
{
int u=q.front(); q.pop();
for(int i=head[u];i;i=edge[i].nxt)
{
int v=edge[i].to;
if(edge[i].dis&&dis[v]==0x3f3f3f3f)
{
dis[v]=dis[u]+1;
q.push(v);
}
}
}
if(dis[t]==0x3f3f3f3f) return false;
return true;
}
inline int dfs(int x,int t,int f)
{
if(!f||x==t) return f;
int used=0,w;
for(int i=cur[x];i;i=edge[i].nxt)
{
int v=edge[i].to;
cur[x]=i;
if(dis[v]==dis[x]+1&&(w=dfs(v,t,min(edge[i].dis,f))))
{
edge[i].dis-=w;
edge[i^1].dis+=w;
used+=w,f-=w;
if(!f) break;
}
}
return used;
}
inline int dinic(int s,int t)
{
int cur_ans=0;
while(bfs(s,t)) cur_ans+=dfs(s,t,INF);
return cur_ans;
}
inline void pre()
{
for(int i=1;i<=n;i++)
{
maxx[i]=1;
for(int j=1;j<i;j++)
if(node[i].a>node[j].a)
maxx[i]=max(maxx[i],maxx[j]+1);
}
for(int i=1;i<=n;i++) kkk=max(kkk,maxx[i]);
}
inline void add_edge()
{
for(int i=1;i<=n;i++) in[i]=i;
for(int i=n+1;i<=2*n;i++) out[i-n]=i;
for(int i=1;i<=n;i++) add(in[i],out[i],node[i].b),num[i]=t-1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<i;j++)
{
if(maxx[j]+1==maxx[i]&&node[j].a<node[i].a)
add(out[j],in[i],INF);
}
}
for(int i=1;i<=n;i++) if(maxx[i]==1) add(S,in[i],INF);
for(int i=1;i<=n;i++) if(maxx[i]==kkk) add(out[i],T,INF);
}
inline bool cmp(struct Node x,struct Node y){return x.c<y.c;}
inline void solve()
{
for(int i=1;i<=n;i++)
{
int x=node[i].id;
if(check(in[x],out[x])) continue;
ans[++cnt]=x;
while(bfs(T,out[x])) dfs(T,out[x],INF);
while(bfs(in[x],S)) dfs(in[x],S,INF);
edge[num[x]].dis=0,edge[num[x]^1].dis=0;
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
TT=read();
while(TT--)
{
memset(head,0,sizeof(head));
cnt=0,kkk=0;
t=1;
n=read();
for(int i=1;i<=n;i++) node[i].a=read();
for(int i=1;i<=n;i++) node[i].b=read();
for(int i=1;i<=n;i++) node[i].c=read();
pre();
add_edge();
printf("%d ",dinic(S,T));
for(int i=1;i<=n;i++) node[i].id=i;
sort(&node[1],&node[n+1],cmp);
solve();
printf("%d\n",cnt);
sort(&ans[1],&ans[cnt+1]);
for(int i=1;i<=cnt;i++) printf("%d ",ans[i]); puts("");
}
return 0;
}