bzoj 3532: [Sdoi2014]Lis

Description

給定序列A,序列中的每一項Ai有刪除代價Bi和附加屬性Ci。請刪除若
幹項,使得4的最長上升子序列長度減少至少1,且付出的代價之和最小,並輸出方案。
如果有多種方案,請輸出將刪去項的附加屬性排序之後,字典序最小的一種。

Solution

首先可以建圖求出最小的代價和 .
由於是刪除點 , 所以我們可以要拆點 .
於是分成兩個集合 \(i,i+n\) .
如果 \(f[i]=1\) , \(S->i\)
如果 \(f[i]=maxf\) \(i+n->T\)
如果 \(f[i]=f[j]+1\&\&a[j]<a[i]\) , \(i+n->i\)
最小割就是答案 .
然後考慮構造字典序最小的解 , 首先按 \(C_i\) 排序 , 從小到大考慮 .
一條邊能夠加入最小割顯然要加入 , 我們就需要判斷這條邊是否存在最小割集中就行了 .
一種方法是減小 \(i->i+n\) 這條邊 \(1\) 的流量 , 如果最小割也減少 \(1\) 那麼就可以成爲最小割的一條邊
也可以通過判斷是否存在 \(i->i+n\) 的增廣路 , 如果存在則不會在最小割集中 .
這樣的話我們刪除一條邊 \((u,v,c)\) 後要消除它的影響 , 爲了節省複雜度 .
\(maxflow(T,v,c),maxflow(u,S,c)\) 再把這條邊容量清零 , 最後再跑一邊 \(maxflow(S,T,inf)\) 就好了.
但是這個題的圖有特殊性質 , 所以只需要 \(maxflow(T,v),maxflow(u,S)\) 就好了.
注意常數優化 :
比如 \(bfs\) 的時候要在遍歷到 \(T\) 時就 $return $ .

#include<bits/stdc++.h>
using namespace std;
template<class T>void gi(T &x){
    int f;char c;
    for(f=1,c=getchar();c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
    for(x=0;c<='9'&&c>='0';c=getchar())x=x*10+(c&15);x*=f;
}
const int N=1550,inf=2e9;
int n,c[N],id[N],a[N],b[N],f[N];
int dis[N*N],S=0,T,head[N],nxt[N*N],to[N*N],num=1,dep[N],ans[N];
inline void link(int x,int y,int z){
    nxt[++num]=head[x];to[num]=y;head[x]=num;dis[num]=z;
    nxt[++num]=head[y];to[num]=x;head[y]=num;dis[num]=0;
}
inline bool comp(const int &i,const int &j){return c[i]<c[j];}
inline bool bfs(int s,int t){
    queue<int>Q;
    for(int i=2*n+1;i>=0;i--)dep[i]=0;
    Q.push(s);dep[s]=1;
    while(!Q.empty()){
        int x=Q.front();Q.pop();
        for(int i=head[x];i;i=nxt[i]){
            if(dis[i]<=0 || dep[to[i]])continue;
            int u=to[i];
            dep[u]=dep[x]+1;Q.push(u);
            if(u==t)return 1;
        }
    }
    return dep[t];
}
inline int dfs(int x,int flow){
    if(x==T || !flow)return flow;
    int tot=0,t;
    for(int i=head[x];i;i=nxt[i]){
        int u=to[i];
        if(dis[i]<=0 || dep[u]!=dep[x]+1)continue;
        t=dfs(u,min(flow,dis[i]));
        dis[i]-=t;dis[i^1]+=t;tot+=t;flow-=t;
        if(!flow)break;
    }
    if(!tot)dep[x]=-1;
    return tot;
}
inline int Dinic(int s,int t){
    S=s;T=t;
    int tot=0,x;
    while(bfs(s,t)){
        x=dfs(s,inf);
        while(x)tot+=x,x=dfs(s,inf);
    }
    return tot;
}
inline void work(){
    int cnt=0;
    memset(head,0,sizeof(head));num=1;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)gi(a[i]);
    for(int i=1;i<=n;i++)gi(b[i]);
    for(int i=1;i<=n;i++)gi(c[i]),id[i]=i;
    sort(id+1,id+n+1,comp);
    int maxf=1;
    for(int i=1;i<=n;i++){
        f[i]=1;
        for(int j=1;j<i;j++)if(a[j]<a[i])f[i]=max(f[i],f[j]+1);
        maxf=max(maxf,f[i]);
    }
    for(int i=1;i<=n;i++){
        if(f[i]==1)link(0,i,inf);
       if(f[i]==maxf)link(i+n,2*n+1,inf);
        for(int j=1;j<i;j++)
            if(f[j]+1==f[i] && a[j]<a[i])link(j+n,i,inf);
        link(i,i+n,b[i]);
    }
    int sum=Dinic(0,2*n+1);
    for(int i=1;i<=n;i++){
        int u=id[i],v=id[i]+n;
        if(bfs(u,v))continue;
        Dinic(2*n+1,v);Dinic(u,0);
        ans[++cnt]=u;
    }
    sort(ans+1,ans+cnt+1);
    printf("%d %d\n",sum,cnt);
    printf("%d",ans[1]);
    for(int i=2;i<=cnt;i++)printf(" %d",ans[i]);
}
int main(){
  freopen("pp.in","r",stdin);
  freopen("pp.out","w",stdout);
  int T;cin>>T;
  while(T--)work(),T?puts(""):0;
  return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章