UOJ Logo zhouzixuan的博客

博客

bzoj 3532: [Sdoi2014]Lis

2016-02-14 16:25:24 By zhouzixuan
【题目描述】:
    给定序列A,序列中的每一项Ai有删除代价Bi和附加属性Ci。请删除若干项,使得4的最长上升子序列长度减少至少1,且付出的代价之和最小,并输出方案。
    如果有多种方案,请输出将删去项的附加属性排序之后,字典序最小的一种。
【题目解法】:
    首先第一问是裸的最小割
    我们用f[i]表示以i结尾的最长上升子序列长度
    然后将每个点i拆成i和i'
    对于f[i]=1的点,连边(s,i,INF)
    对于f[i]=MaxF点的,连边(i',t,INF)
    然后对于每个点i,连边(i,i',b[i])
    对于两个点i,j满足a[j]<a[i]且f[j]+1=f[i],连边(j',i,INF)
    最小割就是答案,这样就有30pt
    考虑构造字典序最小的方案,我们按照C排序,然后贪心的加入
    考虑什么情况下某条边(i,i')一定在某个最小割中?
    很简单,两个条件:
    1.(i,i')满流(显然)
    2.我们强制割开i i',如果最小割不变,那么(i,i')一定在某个最小割中
    如何强制割开某条边呢?我们连边(s,i,INF) (i',t,INF),这样就可以强制将i留在s割,i'留在t割,自然这条边就割开了
    但是这样判断每次都要跑最大流,会TLE...
    我们考虑如果i->i'之间如果存在增广路,那么一定有s->i->i'->t的增广路,此时最小割会增大
    如果i->i'之间没有增广路,那么最小割不会增大。
    因此我们O(N)判断一下i->i'是否存在增广路即可
    然后考虑删去(i,i')对图的影响,我们显然需要把(i,i')对原图的影响恢复回去
    也就是说把流过(i,i')的流退回去即可
    那么我们Sap(t,i') Sap(i,s),然后把(i,i')删去即可
    然后TLE辣TAT
    不虚,我会卡常数...
    QwQ...慢慢都是泪啊
    我们考虑推流是一定退了b[i],因此Sap时判断最大流是否已经等于b[i]了
    然后就过了TAT
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N=2005;
const int M=2000005;
const int INF=32083208;
struct edge {int x,y,op,next; bool flag;} b[M];
int cases,n,S,T,id[N][2],A[N],B[N],C[N],f[N],DP;
int tot,a[N],Id[N],maxflow,ans,Ans[N],pos[N],aim;
inline long long getint()
{
    long long x=0; char c=getchar(); bool flag=false;
    while ((c!='-')&&((c<'0')||(c>'9'))) c=getchar();
    if (c=='-') flag=true,c=getchar();
    while ((c>='0')&&(c<='9')) x=x*10+(long long)(c-'0'),c=getchar();
    if (flag) return -x; else return x;
}
inline void addedge(int x,int y,int z)
{
    ++tot; b[tot].x=y; b[tot].y=z; b[tot].op=tot+1; b[tot].flag=false; b[tot].next=a[x]; a[x]=tot;
    ++tot; b[tot].x=x; b[tot].y=0; b[tot].op=tot-1; b[tot].flag=false; b[tot].next=a[y]; a[y]=tot;
}
void init()
{
    n=getint(); f[0]=0; DP=0; ans=0; aim=INF;
    for (int i=1; i<=n; i++) A[i]=getint();
    for (int i=1; i<=n; i++) B[i]=getint();
    for (int i=1; i<=n; i++) C[i]=getint();
    for (int i=1; i<=n; i++) f[i]=1;
    for (int i=1; i<=n; i++) for (int j=1; j<i; j++) if (A[j]<A[i]) f[i]=max(f[i],f[j]+1);
    for (int i=1; i<=n; i++) DP=max(DP,f[i]);
}
void build()
{
    S=0; T=0; tot=0; memset(a,0,sizeof(a));
    for (int i=1; i<=n; i++) id[i][0]=++T,id[i][1]=++T; ++T;
    for (int i=1; i<=n; i++) addedge(id[i][0],id[i][1],B[i]),Id[i]=tot-1;
    for (int i=1; i<=n; i++) if (f[i]==1) addedge(S,id[i][0],INF);
    for (int i=1; i<=n; i++) if (f[i]==DP) addedge(id[i][1],T,INF);
    for (int i=1; i<=n; i++) for (int j=1; j<i; j++) if ((A[j]<A[i])&&(f[j]+1==f[i])) addedge(id[j][1],id[i][0],INF);
}
namespace NetWork
{
    int pre[N],gap[N],cur[N],dis[N],now;
    inline void augment(int s,int t)
    {
        int flow=INF;
        for (int i=s; i!=t; i=b[cur[i]].x) flow=min(flow,b[cur[i]].y);
        for (int i=s; i!=t; i=b[cur[i]].x) b[cur[i]].y-=flow,b[b[cur[i]].op].y+=flow;
        maxflow+=flow; now=s;
    }
    void Sap(int s,int t)
    {
        for (int i=S; i<=T; i++) cur[i]=a[i],gap[i]=0,pre[i]=0,dis[i]=0;
        now=s; gap[t]=T; maxflow=0;
        while (dis[s]<=T)
        {
            if (maxflow==aim) return;
            if (now==t) augment(s,t); bool Flag=false;
            for (int p=cur[now];p;p=b[p].next)
            {
                int pp=b[p].x; if ((b[p].y<=0)||(dis[pp]+1!=dis[now])) continue;
                pre[pp]=now; cur[now]=p; Flag=true; now=pp; break;
            }
            if (Flag) continue; cur[now]=a[now];
            gap[dis[now]]--; if (gap[dis[now]]==0) break; dis[now]=T+1;
            for (int p=cur[now];p;p=b[p].next)
            {
                int pp=b[p].x;
                if (b[p].y>0) dis[now]=min(dis[now],dis[pp]+1);
            }
            gap[dis[now]]++; if (now!=s) now=pre[now];
        }
    }
}
namespace GetAns
{
    int head,tail,q[N],times; int v[N];
    inline bool cmp(int x,int y) {return C[x]<C[y];}
    /*bool bfs(int s,int t)
    {
        memset(v,false,sizeof(v));
        v[s]=true; head=0; tail=1; q[1]=s;
        while (head<tail)
        {
            int k=q[++head];
            for (int p=a[k];p;p=b[p].next)
            {
                int pp=b[p].x; if (b[p].y<=0) continue;
                if (pp==t) return true; if (!v[pp]) v[q[++tail]=pp]=true;
            }
        }
        return false;
    }*/
    bool dfs(int s,int t)
    {
        if (v[s]==times) return false; if (s==t) return true; v[s]=times;
        for (int p=a[s];p;p=b[p].next) if ((b[p].y>0)&&(dfs(b[p].x,t))) return true;
        return false;
    }
    void solve()
    {
        memset(v,0,sizeof(v));
        for (int i=1; i<=n; i++) pos[i]=i;
        int flow=0,Maxflow=maxflow;
        sort(pos+1,pos+n+1,cmp); printf("%d ",maxflow);
        for (int i=1; i<=n; i++)
        {
            int now=pos[i],x=id[now][0],y=id[now][1];
            if (b[Id[now]].y!=0) continue; ++times; if (dfs(x,y)) continue;
            Ans[++ans]=now; flow+=B[now]; if (flow==Maxflow) break;
            aim=B[now]; NetWork::Sap(T,y); NetWork::Sap(x,S);
            b[Id[now]].y=0; b[b[Id[now]].op].y=0;
        }
        sort(Ans+1,Ans+ans+1); printf("%d\n",ans);
        for (int i=1; i<=ans-1; i++) printf("%d ",Ans[i]); printf("%d\n",Ans[ans]);
    }
}
int main()
{
    cases=getint();
    while (cases--)
    {
        init();
        build();
        NetWork::Sap(S,T);
        GetAns::solve();
    }
    return 0;
}

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。