【SDOI2014】Lis(最小割+退流)

Problem 3532. – [Sdoi2014]Lis

3532: [Sdoi2014]Lis

Time Limit: 10 Sec   Memory Limit: 512 MB
Submit: 1068   Solved: 398
[ Submit][ Status][ Discuss]

Description

 给定序列A,序列中的每一项Ai有删除代价Bi和附加属性Ci。请删除若
干项,使得4的最长上升子序列长度减少至少1,且付出的代价之和最小,并输出方案。
    如果有多种方案,请输出将删去项的附加属性排序之后,字典序最小的一种。
  

Input

  输入包含多组数据。
    输入的第一行包含整数T,表示数据组数。接下来4*T行描述每组数据。
    每组数据的第一行包含一个整数N,表示A的项数,接下来三行,每行N个整数A1..An,B1.,Bn,C1..Cn,满足1 < =Ai,Bi,Ci < =10^9,且Ci两两不同。

Output

    对每组数据,输出两行。第一行包含两个整数S,M,依次表示删去项的代价和与数量;接下来一行M个整数,表示删去项在4中的的位置,按升序输出。

Sample Input

1
6
3 4 4 2 2 3
2 1 1 1 1 2
6 5 4 3 2 1

Sample Output

4 3
2 3 6
解释:删去(A2,43,A6),(A1,A6),(A2,43,44,A5)等都是合法的方案,但
{A2,43,A6)对应的C值的字典序最小。

HINT

1 < =N < =700     T < =5

Source

[ Submit][ Status][ Discuss]

HOME Back

思路+分析:

    P.S. LaTeX好像挂了..将就着看吧QAQ
    首先我们知道,这道题要是没有最小字典序的限制的话,那就是一个裸的最小割。
    但是,他有了最小字典序的限制,就不能是简简单单的一个最小割就是了,虽然第一问还是可以直接用最小割做。
    我们考虑Lis DP的过程,(由于这里n<=700所以我们可以直接 O(n2) O ( n 2 ) 求Lis。)对于Lis DP的过程,其实就是一个DAG,起点就是 f[0]=0 f [ 0 ] = 0 ,终点就是每个 f[i]==max(f[1],f[2]...f[n]) f [ i ] == m a x ( f [ 1 ] , f [ 2 ] . . . f [ n ] ) 的点,由于这样的点有很多个,我们就需要建一个超级汇点,并且把 f[0]=0 f [ 0 ] = 0 当成一个超级源点。这样,我们对于所有的 i<j,a[i]<a[j],f[i]+1==f[j] i < j , a [ i ] < a [ j ] , f [ i ] + 1 == f [ j ] 的点,都 i>j i − > j 连边,然后求出最小割就是第一问的答案。
    对于第二个问题,我们先随便求出一个最小割,然后考虑什么样的边能成为割边。第一个必要条件就是满流,并且如果我们把这条边断掉,那么这条路径上的其他边就没有选的必要了。这样的话如果我们选了一条边 u>v u − > v ,那么我们就需要把 s>u s − > u v>t v − > t 的路径上的流量退掉,这样就一定不会选择这条路上的其他边了(这就是退流的思想)。现在要求字典序最小,那么我们就从小到大枚举边,然后判断这条边是否为割边:满流并且不存在增广路。如果符合就选择它,并且把这条路径上的流量退掉。这样我们就可以解决第二问了。
    P.S. 好久没打最大流的手都生疏了,还调了我好久…

Code

#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
bool Finish_read;
template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('\n');}
template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
/*================Header Template==============*/
const int maxn=7005,oo=0x3f3f3f3f;
struct Edge {
    int from,to,cap,flow;
    Edge(){}
    Edge(int a,int b,int c,int d):from(a),to(b),cap(c),flow(d){}
};
struct node {
    int l,id;
    inline bool operator < (const node &rhs) const {
        return l<rhs.l;
    }
}c[maxn];
int n,m,s,t,T;
vector<Edge>edges;
vector<int>G[maxn],ans;
int d[maxn],cur[maxn],f[maxn],mx,cnt,a[maxn],b[maxn];
inline void init() {
    ans.clear();
    mx=0;
    memset(f,0,sizeof f);
    for(int i=1;i<maxn;i++)
        G[i].clear();
    edges.clear();
}
inline void addedge(int from,int to,int cap) {
    edges.emplace_back(Edge(from,to,cap,0));
    edges.emplace_back(Edge(to,from,0,0));
    G[from].emplace_back(edges.size()-2);
    G[to].emplace_back(edges.size()-1);
}
inline bool bfs(int s,int t) {
    memset(d,-1,sizeof d);
    queue<int>q;
    q.push(s);
    d[s]=0;
    while(!q.empty()) {
        int x=q.front();
        q.pop();
        for(unsigned i=0;i<G[x].size();i++) {
            Edge &e=edges[G[x][i]];
            if(d[e.to]==-1&&e.cap>e.flow) {
                d[e.to]=d[x]+1;
                q.push(e.to);
            }
        }
    }
    return d[t]!=-1;
}
inline int dfs(int x,int a,int s,int t) {
    if(x==t||a==0)
        return a;
    int flow=0,f;
    for(int& i=cur[x];i<(int)G[x].size();i++) {
        Edge &e=edges[G[x][i]];
        if(d[e.to]==d[x]+1&&(f=dfs(e.to,min(a,e.cap-e.flow),s,t))>0) {
            e.flow+=f;
            edges[G[x][i]^1].flow-=f;
            flow+=f;
            a-=f;
            if(a==0)
                break;
        }
    }
    return flow;
}
inline int solve(int s,int t) {
    int ans=0;
    while(bfs(s,t)) {
        memset(cur,0,sizeof cur);
        ans+=dfs(s,oo,s,t);
    }
    return ans;
}
int main() {
    read(T);
    while(T--) {
        init();
        read(n);
        s=2*n+1;
        t=s+1;
        for(int i=1;i<=n;i++)
            read(a[i]);
        for(int i=1;i<=n;i++)
            read(b[i]);
        for(int i=1;i<=n;i++)
            read(c[i].l),c[i].id=i;
        for(int i=1;i<=n;i++) {
            for(int j=0;j<i;j++)
                if(a[j]<a[i])
                    f[i]=max(f[i],f[j]+1);
            mx=max(mx,f[i]);
        }
        for(int i=1;i<=n;i++)
            addedge(i,i+n,b[i]);
        for(int i=1;i<=n;i++)
            if(f[i]==mx)
                addedge(i+n,t,oo);
        for(int i=1;i<=n;i++)
            if(f[i]==1)
                addedge(s,i,oo);
        for(int i=1;i<=n;i++)
            for(int j=1;j<i;j++)
                if(f[i]==f[j]+1&&a[i]>a[j])
                    addedge(j+n,i,oo);
        ll mc=solve(s,t);
        sort(c+1,c+n+1);
        for(int i=1,x;i<=n;i++) {
            x=c[i].id;
            if(edges[(x-1)<<1].flow!=edges[(x-1)<<1].cap||bfs(x,x+n))
                continue;
            //cout<<"!!!"<<x<<endl;
            ans.push_back(x);
            solve(t,x+n),solve(x,s);
            edges[(x-1)<<1].flow=edges[(x-1)<<1].cap;
            edges[((x-1)<<1)^1].flow=edges[((x-1)<<1)^1].cap;
        }
        sort(ans.begin(),ans.end());
        printf("%lld %d\n",mc,ans.size());
        for(int i=0;i<ans.size();i++)
            printf("%d%c",ans[i],i==ans.size()-1?'\n':' ');
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值