P3308 [SDOI2014]LIS
一道网络流建模+贪心求割边边集好题。
从数据范围以及求最长上升子序列相关,不难想到这道题。
具体的,考虑用一个流量来表示一个最长上升子序列。
一个较为显然的结论是,求出 显然很显然,即我们的最长上升子序列的下标序列
那么如果我们将所有的点满足
容易发现这题允许破坏的是”点“,那么最终的建图也基本明朗了起来。
1.将这
2.将所有满足
3.将所有的点满足
最后跑一遍
再考虑如何确定出附加属性字典序最小的最小割割边边集。
首先容易发现的是,成为割边的一定是”入点连向对应出点的边“,这显然是我们一手策划的,将题目中的
具体实现的时候也不需要其他题解那么麻烦,判断一条边是否为割边,显然就是从该边的入点无法到达出点(当然不走流量为
最后将得到的”需破坏点“下标
复杂度就是 实际上哪里都不满
最后放一下代码
inline void add(rint x,rint y,rint flow)
{
e[++cnt]=(Edge){y,head[x],flow},head[x]=cnt;
}
int bfs(rint s,rint t)
{
memset(dis,0,sizeof(dis)),memcpy(tmphead,head,sizeof(head));
queue<int> q;q.push(s),dis[s]=1;
while(!q.empty())
{
rint u=q.front();q.pop();
for(rint i=head[u];i;i=e[i].next)
{
rint v=e[i].to;
if(!e[i].flow||dis[v]) continue;
dis[v]=dis[u]+1,q.push(v);
if(v==t) return 1;
}
}
return 0;
}
int dfs(rint x,rint t,rint flow)
{
if(x==t) return flow;
rint rest=flow;
for(rint i=tmphead[x];i;i=e[i].next)
{
tmphead[x]=i;rint v=e[i].to;
if(!e[i].flow||dis[v]!=dis[x]+1) continue;
rint tmp=dfs(v,t,min(rest,e[i].flow));
tmp?e[i].flow-=tmp,e[i^1].flow+=tmp,rest-=tmp:dis[v]=0;
if(!rest) return flow;
}
return flow-rest;
}
inline void add_edge(rint x,rint y,rint flow)
{
add(x,y,flow),add(y,x,0);
}
int main()
{
for(rint tt=read<int>();tt;tt--)
{
rint n=read<int>(),mx=0,s=0,t=n*2+1;memset(head,0,t+1<<2),cnt=1;
for(rint i=1;i<=n;i++) a[i]=read<int>();
for(rint i=1;i<=n;i++)
{
dp[i]=1;
for(rint j=1;j<i;j++)
if(a[i]>a[j])
dp[i]=max(dp[i],dp[j]+1);
mx=max(mx,dp[i]);
}
for(rint i=1;i<=n;i++) add_edge(i,i+n,read<int>());
for(rint i=1;i<=n;i++)
{
if(dp[i]==1) add_edge(s,i,inf);
if(dp[i]==mx) add_edge(i+n,t,inf);
for(rint j=i+1;j<=n;j++)
if(a[i]<a[j]&&dp[j]==dp[i]+1)
add_edge(i+n,j,inf);
}
for(rint i=1;i<=n;i++) c[i]=(Node){read<int>(),i};
sort(c+1,c+n+1,[](const Node &x,const Node &y){return x.val<y.val;});
rll ans=0;rint N=0;
while(bfs(s,t)) ans+=dfs(s,t,inf);
for(rint i=1;i<=n;i++)
{
rint u=c[i].id,v=u+n;
if(!bfs(u,v))
{
b[++N]=u,e[u*2].flow=e[u*2+1].flow=0;
for(rint i=2;i<=cnt;i+=2) e[i].flow+=e[i^1].flow,e[i^1].flow=0;
while(bfs(s,t)) dfs(s,t,inf);
}
}
sort(b+1,b+N+1),printf("%lld %d\n",ans,N);
for(rint i=1;i<=N;i++) printf("%d ",b[i]);puts("");
}
return 0;
}