• 【洛谷3308】[SDOI2014] LIS(网络流)


    点此看题面

    • 给定一个长度为(n)的序列(a_i),每个元素有一个删除代价(b_i)和附加属性(c_i)(附加属性互不相同)。
    • 求最小的删除代价使得最长上升子序列长度至少减(1),并求出代价最小的一个方案,使得删除元素的附加属性排序后的字典序尽可能小。
    • 数据组数(le5,nle700)

    (LIS)经典建图

    众所周知(LIS)有一个经典的建图,设(f_i)表示以(i)为结尾的最长上升子序列长度。

    (j)(i)连边,当且仅当(j<i,a_j<a_i,f_j+1=f_i)。特殊地,超级源向所有(f_i=1)的点连边,(f_i=max_{k=1}^n f_k)的点向超级汇连边。

    然后这道题删除一个点有一个代价,我们就拆点,在(i)的入点和出点之间连一条容量为(b_i)的边,其余不同位置的点之间的连边容量直接设为(INF)表示不可删去。

    然后只要跑一遍网络流求最小割就能得到最小删除代价了。

    附加属性字典序最小的方案

    显然从小到大枚举(c_k),判断(k)能否被选在最小割集中。

    这个判断只需调用一下(BFS)函数判一下是否存在一条从(k)的入点到出点的增广路,有的话则无法选在最小割集中。

    而选中了一条边就需要消除它的影响,即退流,这只要从超级汇向出点、从入点向超级源这样跑两次与原本反向的网络流即可。然后把这条边及其反向边的容量都设为(0)彻底抹杀。

    代码:(O(Dinic))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 700
    #define INF 1e9
    using namespace std;
    int n,a[N+5],b[N+5],c[N+5],f[N+5],ans[N+5];pair<int,int> p[N+5];
    namespace Dinic//网络流
    {
    	#define PS (2*N+2)
    	#define ES (N*N+2*N)
    	#define E(x) ((((x)-1)^1)+1)
    	#define add(x,y,f) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].F=f)
    	int ee,lnk[PS+5],cur[PS+5];struct edge {int to,nxt,F;}e[2*ES+5];
    	I void Cl() {ee=0;for(RI i=1;i<=2*n+2;++i) lnk[i]=0;} 
    	I void Add(CI x,CI y,CI f) {add(x,y,f),add(y,x,0);}
    	int q[PS+5],D[PS+5];I bool BFS(CI s,CI t)//BFS求增广路
    	{
    		RI i,k,H=1,T=1;for(i=1;i<=2*n+2;++i) D[i]=-1;D[q[1]=s]=0;
    		W(H<=T&&!~D[t]) for(i=lnk[k=q[H++]];i;i=e[i].nxt)
    			e[i].F&&!~D[e[i].to]&&(D[q[++T]=e[i].to]=D[k]+1);return ~D[t];
    	}
    	I int DFS(CI x,CI t,RI f=INF)//DFS增广
    	{
    		if(x==t||!f) return f;RI i,o,g=0;for(i=cur[x];i;i=e[i].nxt)
    		{
    			if(D[e[i].to]^(D[x]+1)||!(o=DFS(e[i].to,t,min(f,e[i].F)))) continue;
    			if(g+=o,e[i].F-=o,e[E(i)].F+=o,!(f-=o)) break;
    		}return cur[x]=i,g;
    	}
    	I int MaxFlow(CI s,CI t) {RI f=0;W(BFS(s,t)) memcpy(cur,lnk,sizeof(cur)),f+=DFS(s,t);return f;}//最大流
    }
    int main()
    {
    	#define s 2*n+1//超级源
    	#define t 2*n+2//超级汇
    	RI Tt,i,j,k,ct,Mx;scanf("%d",&Tt);W(Tt--)
    	{
    		for(scanf("%d",&n),Dinic::Cl(),i=1;i<=n;++i) scanf("%d",a+i);
    		for(i=1;i<=n;++i) scanf("%d",b+i),Dinic::Add(i,n+i,b[i]);//入点连向出点
    		for(i=1;i<=n;++i) scanf("%d",c+i),p[i]=make_pair(c[i],i);sort(p+1,p+n+1);//按附加属性排序
    		for(Mx=0,i=1;i<=n;Mx=max(Mx,f[i++])) for(f[i]=j=1;j^i;++j) a[i]>a[j]&&(f[i]=max(f[i],f[j]+1));//预处理DP
    		for(i=1;i<=n;++i)
    		{
    			f[i]==1&&(Dinic::Add(s,i,INF),0),f[i]==Mx&&(Dinic::Add(n+i,t,INF),0);//与超级源汇的连边
    			for(j=1;j^i;++j) a[i]>a[j]&&f[i]==f[j]+1&&(Dinic::Add(n+j,i,INF),0);//分层建图
    		}
    		for(printf("%d ",Dinic::MaxFlow(s,t)),ct=0,i=1;i<=n;++i)
    		{
    			if(k=p[i].second,Dinic::BFS(k,n+k)) continue;ans[++ct]=k;//判断有没有增广路
    			Dinic::MaxFlow(t,n+k),Dinic::MaxFlow(k,s),Dinic::e[2*k-1].F=Dinic::e[2*k].F=0;//退流,删边
    		}
    		for(printf("%d
    ",ct),sort(ans+1,ans+ct+1),i=1;i<=ct;++i) printf("%d%c",ans[i]," 
    "[i==ct]);
    	}return 0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    Scala 基础语法(二)
    Scala 基础语法(一)
    Scala 概述+scala安装教程+IDEA创建scala工程
    树链剖分【p2590】[ZJOI2008]树的统计
    树链剖分【p1505】[国家集训队]旅游
    状压DP【p1896】[SCOI2005]互不侵犯
    树链剖分【P3833】 [SHOI2012]魔法树
    KMP【UVA1328】 Period
    Trie树【UVA11362】Phone List
    线段树【p2801】教主的魔法
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu3308.html
Copyright © 2020-2023  润新知