題目
分析
先做一個最長上升子序列的DP,然後按照轉移的父子順序連邊,要拆點,具體來說:
- 所有向連容量爲的邊;
- 若,向連連容量爲的邊;
- 若是所有中最大的之一,向連容量爲的邊;
- 若,向連容量爲的邊。
樣例如下:
我之前沒有拆點建圖,當兩個點連向同一個點的時候就會有問題,要刪兩條邊……
接下來是字典序最小的問題。先按值從小到大排序,枚舉每條到的邊,顯然若當前邊可刪(即當前邊在的殘留容量爲),就一定要刪它。刪邊時,先退流,再把這條邊的流量抹去即可。退流,就是把這條邊所在的增廣路的流量返回去,直接Dinic(i,S),Dinic(T,i')
即可。抹去,就是把這條邊的正反容量都置爲。
還有一個細節,Dinic裏面的bfs
,找到點就直接返回,不要等循環完了才返回,不然會T。
代碼
實現上有一些減少代碼量的細節,例如運利用bfs
判斷到的殘留容量,直接用id<<1
訪問到的邊等。
#include<queue>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
int read(){
int x=0;bool f=0;char c=getchar();
while(c<'0'||c>'9') f|=c=='-',c=getchar();
while(c>='0'&&c<='9') x=x*10+(c^48),c=getchar();
return f?-x:x;
}
#define MAXN 50000
#define MAXM 1000000
#define INF int(2e9)
#define LL long long
struct Edge{
int v,w,Next;
}E[MAXM+5];
int Adj[MAXN+5],ecnt;
void AddEdge(int u,int v,int w){
E[ecnt].v=v,E[ecnt].w=w,E[ecnt].Next=Adj[u],Adj[u]=ecnt++;
E[ecnt].v=u,E[ecnt].w=0,E[ecnt].Next=Adj[v],Adj[v]=ecnt++;
}
int Cur[MAXN+5];
int Dist[MAXN+5];
bool bfs(int S,int T,int N){
for(int i=0;i<=N;i++)//N這裏我以前一直寫的T,知道今天我調了半天最後發現自己是hape,這道題裏面不能寫T
Cur[i]=Adj[i],Dist[i]=-1;
queue<int> Q;
Q.push(S),Dist[S]=0;
while(!Q.empty()){
int u=Q.front();Q.pop();
for(int i=Adj[u];i!=-1;i=E[i].Next){
int v=E[i].v;
if(E[i].w&&Dist[v]==-1){
Dist[v]=Dist[u]+1;
Q.push(v);
if(v==T)//就是這裏,出去再判就T了
return 1;
}
}
}
return 0;
}
int dfs(int u,int T,int Min){
if(!Min||u==T)
return Min;
int Rest=Min,Now;
for(int &i=Cur[u];i!=-1;i=E[i].Next){
int v=E[i].v,w=E[i].w;
if(w&&Dist[v]==Dist[u]+1&&(Now=dfs(v,T,min(Rest,w)))){
Rest-=Now,
E[i].w-=Now,
E[i^1].w+=Now;
if(!Rest)
break;
}
}
return Min-Rest;
}
long long Dinic(int S,int T,int N){
long long ret=0;
while(bfs(S,T,N))
ret+=dfs(S,T,INF);
return ret;
}
#undef MAXN
#undef MAXM
#define MAXN 700
int N;
int dp[MAXN+5];
int A[MAXN+5],B[MAXN+5],C[MAXN+5];
int ID[MAXN+5];
bool cmp(int i,int j){
return C[i]<C[j];
}
int main(){
int T=read();
while(T--){
N=read();
int S=0,T=2*N+1;
ecnt=0;
for(int i=0;i<=T;i++) Adj[i]=-1;
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();
int Ans=0;
for(int i=1;i<=N;i++){
int Max=0;
for(int j=1;j<i;j++)
if(A[j]<A[i])
Max=max(Max,dp[j]);
Ans=max(Ans,dp[i]=Max+1);
}
for(int i=1;i<=N;i++)
AddEdge(i,i+N,B[i]);//要先加拆點的邊,才能用124和125行訪問邊的技巧
for(int i=1;i<=N;i++){
if(dp[i]==1) AddEdge(0,i,INF);
if(dp[i]==Ans) AddEdge(i+N,T,INF);
for(int j=i+1;j<=N;j++)
if(A[j]>A[i]&&dp[j]==dp[i]+1)
AddEdge(i+N,j,INF);
}
// for(int i=0;i<=N+1;i++)
// for(int j=Adj[i];j!=-1;j=E[j].Next)
// if(E[j].w)
// printf("%d %d %d\n",i,E[j].v,E[j].w);
printf("%lld ",Dinic(S,T,2*N+1));
for(int i=1;i<=N;i++)
ID[i]=i;
sort(ID+1,ID+N+1,cmp);
vector<int> Plan;
for(int i=1;i<=N;i++){
if(!bfs(ID[i],ID[i]+N,2*N+1)){
Plan.push_back(ID[i]);
Dinic(ID[i],S,2*N+1),
Dinic(T,ID[i]+N,2*N+1);//退流
int EdgeID=(ID[i]-1)<<1;
E[EdgeID].w=E[EdgeID^1].w=0;
}
}
sort(Plan.begin(),Plan.end());
printf("%d\n",Plan.size());
for(int i=0;i<int(Plan.size());i++)
printf("%d%c",Plan[i],i==Plan.size()?'\n':' ');
}
}