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