码迷,mamicode.com
首页 > 其他好文 > 详细

SDOI2014 LIS

时间:2019-06-15 15:35:27      阅读:93      评论:0      收藏:0      [点我收藏+]

标签:割边   getc   cst   ems   oid   names   turn   数组   problem   

题目链接:戳我

我们先做一个DP,就能求出来到前i位的最长上升子序列的长度(maxx[i]数组)。

然后我们考虑求最小割——给每个点拆点,如果要割掉这个点,就相当于把in[i]--out[i]这条边给割掉了。

然后如果在最长上升子序列中,该位下面可以接很多,那么就从该位代表的out向下面可以接的in连边。

然后跑最小割就行了QAQ

但是......之后还需要求字典序最小的最小割方案。
那么我们先把边从小到大排个序,然后把所有可能割边都捞出来,贪心地选择。
但是有一个问题,就是我们选择了一个边之后,它会对我们之后的决策产生影响(比如说,本来应该选的边可以不选了)
所以我们要强行退流(具体操作可以看代码)。我们知道选了这条边之后,这条边的可替代品就不需要选了,这些可替代它的边显然是存在于S-u,v-T之间的。所以我们直接退掉他们满流边的流量就行啦!

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define S 0
#define T 2*n+1
#define MAXN 1510
#define INF 0x3f3f3f3f
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch==-1)f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*f;
}
int TT,n,t=1,cnt,kkk;
int num[MAXN];
int ans[MAXN],in[MAXN],out[MAXN],done[MAXN];
int head[MAXN],cur[MAXN],dis[MAXN],dp[MAXN][MAXN],maxx[MAXN];
struct Node{int id,a,b,c;}node[MAXN];
struct Edge{int nxt,to,dis;}edge[MAXN*MAXN*2];
inline void add(int from,int to,int dis)
{
//  printf("[%d %d] %d\n",from,to,dis);
    edge[++t].nxt=head[from],edge[t].to=to,edge[t].dis=dis,head[from]=t;
    edge[++t].nxt=head[to],edge[t].to=from,edge[t].dis=0,head[to]=t;
}
inline bool check(int s,int t)
{
    queue<int>q;
    memset(done,0,sizeof(done));
    q.push(s);done[s]=1;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        if(u==t) return true;
        for(int i=head[u];i;i=edge[i].nxt)
        {
            int v=edge[i].to;
            if(edge[i].dis&&done[v]==0) 
                q.push(v),done[v]=1;
        }
    }
    return false;
}
inline bool bfs(int s,int t)
{
    queue<int>q;
    memset(dis,0x3f,sizeof(dis));
    memcpy(cur,head,sizeof(head));
    q.push(s);dis[s]=0;
    while(!q.empty())
    {
        int u=q.front(); q.pop();
        for(int i=head[u];i;i=edge[i].nxt)
        {
            int v=edge[i].to;
            if(edge[i].dis&&dis[v]==0x3f3f3f3f)
            {
                dis[v]=dis[u]+1;
                q.push(v);
            }
        }
    }
    if(dis[t]==0x3f3f3f3f) return false;
    return true;
}
inline int dfs(int x,int t,int f)
{
    if(!f||x==t) return f;
    int used=0,w;
    for(int i=cur[x];i;i=edge[i].nxt)
    {
        int v=edge[i].to;
        cur[x]=i;
        if(dis[v]==dis[x]+1&&(w=dfs(v,t,min(edge[i].dis,f))))
        {
            edge[i].dis-=w;
            edge[i^1].dis+=w;
            used+=w,f-=w;
            if(!f) break;
        }
    }
    return used;
}
inline int dinic(int s,int t)
{
    int cur_ans=0;
    while(bfs(s,t)) cur_ans+=dfs(s,t,INF);
    return cur_ans;
}
inline void pre()
{
    for(int i=1;i<=n;i++)
    {
        maxx[i]=1;
        for(int j=1;j<i;j++)
            if(node[i].a>node[j].a)
                maxx[i]=max(maxx[i],maxx[j]+1);
    }   
    for(int i=1;i<=n;i++) kkk=max(kkk,maxx[i]);
}
inline void add_edge()
{
    for(int i=1;i<=n;i++) in[i]=i;
    for(int i=n+1;i<=2*n;i++) out[i-n]=i;
    for(int i=1;i<=n;i++) add(in[i],out[i],node[i].b),num[i]=t-1;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<i;j++)
        {
            if(maxx[j]+1==maxx[i]&&node[j].a<node[i].a)
                add(out[j],in[i],INF);
        }
    }
    for(int i=1;i<=n;i++) if(maxx[i]==1) add(S,in[i],INF);
    for(int i=1;i<=n;i++) if(maxx[i]==kkk) add(out[i],T,INF);
}
inline bool cmp(struct Node x,struct Node y){return x.c<y.c;}
inline void solve()
{
    for(int i=1;i<=n;i++)
    {
        int x=node[i].id;
        if(check(in[x],out[x])) continue;
        ans[++cnt]=x;
        while(bfs(T,out[x])) dfs(T,out[x],INF);
        while(bfs(in[x],S)) dfs(in[x],S,INF);
        edge[num[x]].dis=0,edge[num[x]^1].dis=0;
    }
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    TT=read();
    while(TT--)
    {
        memset(head,0,sizeof(head));
        cnt=0,kkk=0;
        t=1;
        n=read();
        for(int i=1;i<=n;i++) node[i].a=read();
        for(int i=1;i<=n;i++) node[i].b=read();
        for(int i=1;i<=n;i++) node[i].c=read();
        pre();
        add_edge();
        printf("%d ",dinic(S,T));
        for(int i=1;i<=n;i++) node[i].id=i;
        sort(&node[1],&node[n+1],cmp);
        solve();
        printf("%d\n",cnt);
        sort(&ans[1],&ans[cnt+1]);
        for(int i=1;i<=cnt;i++) printf("%d ",ans[i]); puts("");
    }
    return 0;
}

SDOI2014 LIS

标签:割边   getc   cst   ems   oid   names   turn   数组   problem   

原文地址:https://www.cnblogs.com/fengxunling/p/11005895.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!