其他分享
首页 > 其他分享> > Luogu3308 [SDOI2014]LIS

Luogu3308 [SDOI2014]LIS

作者:互联网

Luogu3308 [SDOI2014]LIS

网络流

很容易构建网络流模型,对于所有\(i\)满足存在以\(i\)开头的最长上升子序列,由源点\(S\)向\(i\)连边。

对于所有\(i\)满足存在以\(i\)结尾的最长上升子序列,由汇点\(T\)向\(i\)连边。

对于\(i,j,a_j<a_i,dp_i=dp_j+1\),连边\(i \rightarrow j\),保证两点间只有最长上升子序列会连边。

由于是删除点,可以拆点得到。

那么如何求字典序最小割?

需先了解可行边。

可行边:\((u,v)\)满流且不存在其他\(u \rightarrow v\)的路径(也就是说\(u\)不能增广到\(v\),或\(u,v\)不在同一个强连通分量中)。

(附)必需边:\((u,v)\)满流且不存在其他\(u \rightarrow v\)的路径,同时存在\(S \rightarrow u,v \rightarrow T\)的路径(也就是说\(u\)不能增广到\(v\)但能够从\(S\)增广到\(u\),同时能够从\(v\)增广到\(T\);也可以说\(u,v\)不在同一个强连通分量中,\(u,S\)在同一强连通分量中,\(v,T\)在同一强连通分量中)。

根据最大流=最小割,跑一遍即可计算出最小代价。

那么字典序最小割方案?

利用这里的结论,我们每次按字典序枚举边,判断是否是可行边,然后退流。

那么这题就解决了。

其实蒟蒻对于退流还是有点疑惑的,以及为何本题中退流后不用继续跑一遍\(S \rightarrow T\)的最大流依然存疑。

\(Code:\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define N 705
#define ll long long
using namespace std;
const ll INF=1919191919191919;
int tr;
int n,s,t,S,T,mx=0;
struct node
{
    int a,b,c,id;
    bool operator < (const node &A) const
    {
        return c<A.c;
    }
}q[N];
struct edge
{
    int nxt,v;
    ll cap;
    edge (int Nxt=0,int V=0,ll Cap=0)
    {
        nxt=Nxt,v=V,cap=Cap;
    }
}e[N*N*4];
int dp1[N],dp2[N];
int dep[N << 1],pt[N];
int tot=1,fr[N << 1],st[N << 1];
bool vis[N << 1];
ll ans=0;
void add(int x,int y,ll z)
{
    ++tot;
    e[tot]=edge(fr[x],y,z),fr[x]=tot;
}
void add_edge(int x,int y,ll z)
{
    add(x,y,z),add(y,x,0);
}
bool bfs()
{
    memset(dep,-1,(T+1)*sizeof(int));
    dep[s]=0,st[s]=fr[s];
    queue<int>q;
    q.push(s);
    while (!q.empty())
    {
        int u=q.front();
        q.pop();
        for (int i=fr[u];i;i=e[i].nxt)
        {
            int v=e[i].v,cap=e[i].cap;
            if (!cap || ~dep[v])
                continue;
            dep[v]=dep[u]+1,st[v]=fr[v];
            if (v==t)
                return true;
            q.push(v);
        }
    }
    return false;
}
ll dfs(int u,ll flow)
{
    if (u==t)
        return flow;
    ll res=0;
    for (int i=st[u];i && flow;i=e[i].nxt)
    {
        st[u]=i;
        int v=e[i].v;
        ll cap=e[i].cap;
        if (!cap || dep[v]!=dep[u]+1)
            continue;
        ll k=dfs(v,min(flow,cap));
        e[i].cap-=k;
        e[i^1].cap+=k;
        res+=k;
        flow-=k;
    }
    if (!res)
        dep[u]=-1;
    return res;
}
void Dinic(int S,int T)
{
    s=S,t=T;
    while (bfs())
        ans+=dfs(s,INF);
}
bool check(int u,int t)
{
    if (u==t)
        return true;
    if (vis[u])
        return false;
    vis[u]=true;
    for (int i=fr[u];i;i=e[i].nxt)
    {
        int v=e[i].v,cap=e[i].cap;
        if (!cap)
            continue;
        if (check(v,t))
            return true;
    }
    return false;
}
int main()
{
    scanf("%d",&tr);
    while (tr--)
    {
        scanf("%d",&n);
        for (int i=1;i<=n;++i)
            scanf("%d",&q[i].a),q[i].id=i;
        for (int i=1;i<=n;++i)
            scanf("%d",&q[i].b);
        for (int i=1;i<=n;++i)
            scanf("%d",&q[i].c);
        tot=1;
        S=n+n+1,T=n+n+2;
        for (int i=1;i<=n;++i)
            add_edge(i,i+n,q[i].b),dp1[i]=dp2[i]=1;
        for (int i=1;i<=n;++i)
            for (int j=1;j<i;++j)
                if (q[i].a>q[j].a)
                    dp1[i]=max(dp1[i],dp1[j]+1);
        mx=0;
        for (int i=1;i<=n;++i)
            mx=max(mx,dp1[i]);
        for (int i=n;i;--i)
            for (int j=i+1;j<=n;++j)
                if (q[i].a<q[j].a)
                    dp2[i]=max(dp2[i],dp2[j]+1);
        for (int i=1;i<=n;++i)
        {
            if (dp1[i]==mx)
                add_edge(i+n,T,INF);
            if (dp2[i]==mx)
                add_edge(S,i,INF);
        }
        for (int i=1;i<=n;++i)
            for (int j=1;j<i;++j)
                if (q[i].a>q[j].a && dp1[i]==dp1[j]+1)
                    add_edge(j+n,i,INF);
        ans=0,Dinic(S,T);
        printf("%d ",ans);
        sort(q+1,q+n+1);
        pt[0]=0;
        for (int i=1;i<=n;++i)
        {
            int u=q[i].id;
            memset(vis,0,(T+1)*sizeof(bool));
            if (check(u,u+n))
                continue;
            pt[++pt[0]]=u;
            e[u << 1].cap=0,e[u << 1 | 1].cap=0;
            Dinic(u,S),Dinic(T,u+n);
        }
        printf("%d\n",pt[0]);
        sort(pt+1,pt+pt[0]+1);
        for (int i=1;i<=pt[0];++i)
            printf("%d ",pt[i]);
        putchar('\n');
        memset(fr,0,(T+1)*sizeof(int));
    }
    return 0;
}

标签:return,dp1,Luogu3308,int,ll,cap,dep,SDOI2014,LIS
来源: https://www.cnblogs.com/GK0328/p/14123656.html