BZOJ3532: [Sdoi2014]Lis

19 篇文章 0 订阅

如果不考虑C值的字典序最小的割的话,可以将先dp出以i结尾的LIS长度f[i],然后每个点拆点流量为Bi,对于 j<i,a[j]<a[i],f[j]+1=f[i] j < i , a [ j ] < a [ i ] , f [ j ] + 1 = f [ i ] ,j拆出的出度点连向i的入度点,跑最小割就是最小花费
如果要求字典序最小的割,那么我们可以将C排序后枚举每条边看他能否在最小割中
判一条边(u,v)在不在某个割中,首先他必须满流,然后在残余网络中,只考虑还有流量的边,u和v必须不在同一个强联通中,因为若他们在同一个强联通中,因为反向弧(v,u)是有流量的,说明存在一条u到v的路径仍有流量,那(u,v)就没有割掉,相当于没割
这两个条件若都满足(u,v)可以是某个最小割中的边
我们已经取出了(u,v),考虑退掉他的影响,(u,v)对这个图的影响可以理解为(S,u)+(u,v)+(v,T),去掉(u,v),只需要跑(u,S),(T,v)两次最大流就可以把流退回来,因为删掉的是最小割中的边,退流后的残余网络仍是剩余图的最小割

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define inf 1e15
using namespace std;

inline void down(int &a,const int &b){if(a>b)a=b;}
const int maxn = 3100;
const int maxm = 6100000;

int n;
int A[maxn],B[maxn],C[maxn];
struct node{int i,c;}c[maxn];
inline bool cmp(const node x,const node y){return x.c<y.c;}
int f[maxn];

int S,T,st,ed;
int ei[maxn],use[maxm];
struct edge{int y,nex;ll c;}a[maxm]; int len,fir[maxn],fi[maxn];
inline void ins(const int x,const int y,const ll c)
{
    a[++len]=(edge){y,fir[x],c};fir[x]=len;
    a[++len]=(edge){x,fir[y],0};fir[y]=len;
}

int h[maxn];
queue<int>q;
bool bfs()
{
    for(int i=1;i<=T;i++) h[i]=0;
    h[st]=1; q.push(st);
    while(!q.empty())
    {
        const int x=q.front(); q.pop();
        for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y)
            if(a[k].c&&!h[y]&&!use[k]) h[y]=h[x]+1,q.push(y);
    }
    return h[ed]>0;
}
ll dfs(const int x,const ll flow)
{
    if(x==ed) return flow;
    ll delta=0;
    for(int &k=fi[x],y=a[k].y;k;k=a[k].nex,y=a[k].y)
    {
        if(a[k].c&&h[y]==h[x]+1&&!use[k])
        {
            ll cc=dfs(y,min(flow-delta,a[k].c));
            a[k].c-=cc,a[k^1].c+=cc;
            delta+=cc;
        }
        if(delta==flow) return delta;
    }
    return delta;
}
ll Flow(ll u)
{
    ll re=0;
    while(bfs()) 
    {
        for(int i=1;i<=T;i++) fi[i]=fir[i];
        re+=dfs(st,u);
    }
    return re;
}

int dfn[maxn],low[maxn],id;
int t[maxn],tp; bool insta[maxn];
int bel[maxn],cnt;
void tarjan(const int x)
{
    dfn[x]=low[x]=++id; insta[t[++tp]=x]=true;
    for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(a[k].c&&!use[k])
    {
        if(!dfn[y]) tarjan(y),down(low[x],low[y]);
        else if(insta[y]) down(low[x],dfn[y]);
    }
    if(low[x]==dfn[x])
    {
        ++cnt; int la=0;
        while(la!=x)
        {
            insta[la=t[tp--]]=false;
            bel[la]=cnt;
        }
    }
}

int ans[maxn],ansn;
ll solve()
{
    st=S,ed=T; ll re=Flow(LLONG_MAX);
    ansn=0;
    sort(c+1,c+n+1,cmp);
    bool ok=false;
    for(int i=1;i<=n;i++)
    {
        if(!ok)
        {
            cnt=id=0;
            for(int j=1;j<=T;j++) dfn[j]=0;
            for(int j=1;j<=T;j++) if(!dfn[j]) 
                tarjan(j);
            ok=true;
        }
        int ii=c[i].i;
        int x=ii,y=ii+n;
        if(bel[x]!=bel[y]&&!a[ei[ii]].c)
        {
            ok=false;
            ans[++ansn]=ii;
            use[ei[ii]]=1;
            int u=a[ei[ii]^1].c;
            st=x,ed=S; Flow(u);
            st=T,ed=y; Flow(u);
        }
    }
    return re;
}

int main()
{
    int tcase; scanf("%d",&tcase);
    while(tcase--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&A[i]);
        for(int i=1;i<=n;i++) scanf("%d",&B[i]);
        for(int i=1;i<=n;i++) scanf("%d",&C[i]);

        f[0]=0; int mxf=0;
        for(int i=1;i<=n;i++)
        {
            f[i]=0;
            for(int j=0;j<i;j++) if(A[i]>A[j])
                f[i]=max(f[i],f[j]+1);
            mxf=max(mxf,f[i]);
        }

        S=2*n+1,T=S+1;
        while(len) use[len--]=0;
        len=1;for(int i=1;i<=T;i++) fir[i]=0;

        for(int i=1;i<=n;i++)
        {
            ei[i]=len+1,ins(i,i+n,B[i]);
            c[i].i=i,c[i].c=C[i];
            if(f[i]==1) ins(S,i,inf);
            if(f[i]==mxf) ins(i+n,T,inf);
            for(int j=1;j<i;j++) if(f[j]+1==f[i]&&A[j]<A[i])
                ins(j+n,i,inf);
        }
        printf("%lld ",solve());
        sort(ans+1,ans+ansn+1);
        printf("%d\n",ansn);
        for(int i=1;i<ansn;i++) printf("%d ",ans[i]);
        printf("%d\n",ans[ansn]);
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值