[BZOJ3532] Lis(最大流拆點+退流)

文章目錄

題目

Lis

分析

先做一個最長上升子序列的DP,然後按照轉移的父子順序連邊,要拆點,具體來說:

  • 所有iii(i+N)i'(i+N)連容量爲BiB_i的邊;
  • dp[i]=1dp[i]=1SS向連ii連容量爲\infty的邊;
  • dp[i]dp[i]是所有dp[i]dp[i]中最大的之一,ii'TT連容量爲\infty的邊;
  • dp[j]=dp[i]+1dp[j]=dp[i]+1iijj連容量爲\infty的邊。

樣例如下:
樣例圖示
我之前沒有拆點建圖,當兩個點連向同一個點的時候就會有問題,要刪兩條邊……


接下來是字典序最小的問題。先按CC值從小到大排序,枚舉每條iiii'的邊,顯然若當前邊可刪(即當前邊在的殘留容量爲00),就一定要刪它。刪邊時,先退流,再把這條邊的流量抹去即可。退流,就是把這條邊所在的增廣路的流量返回去,直接Dinic(i,S),Dinic(T,i')即可。抹去,就是把這條邊的正反容量都置爲00


還有一個細節,Dinic裏面的bfs,找到TT點就直接返回,不要等循環完了才返回,不然會T。

代碼

實現上有一些減少代碼量的細節,例如運利用bfs判斷iiii&#x27;的殘留容量,直接用id<<1訪問iiii&#x27;的邊等。

#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':' ');
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章