P3308 [SDOI2014]LIS

这道题目是根据退流知识点找到的,其实之前已经在 CF 上写过一个退流了,但是一直 T ,不是很明白自己是哪里写假了,于是再找了一道退流题写写看。

最长上升子序列好像是有一个网络流模型的。考虑一个序列的最长上升子序列长度是为该序列的最长不上升子序列个数,所以直接每一个点向所有他后面的不大于他的点连边即可。

好像有点问题。。。

于是让我们打开前置题目:

LOJ6005 「网络流 24 题」最长递增子序列

发现我的做法几乎完全歪掉,题解的解法是利用 \(\text{dp}\) 来建网络流的图,然后在这个图上跑最大流。

具体来说,就是考虑先用 \(\text{dp}\) 计算出最长非严格递增子序列的长度 \(s\) ,然后利用 \(f_i\) 数组来建图,具体就是 \(f_i=1\) 的源点向其连边, \(f_i=s\) 的向汇点连边,任意 \(i<j,a_i\le a_j,f_i+1=f_j\)\(i\)\(j\) 连边。然后跑最大流即可,以上流量都为 \(1\) 。然后考虑第三问,就是直接将 \(x_1\)\(x_n\) 的相关流量改成 \(\infty\) 再跑即可。


回到这题

我们再来考虑一下这道题怎么搞。上面那题不会不是这题的前置吧。

那我们如果考虑上面那题的建图的话,我们实际上就是要求一个类似于最小割的东西,使得这个图的 \(S\)\(T\) 必须断开。

考虑求方案的话实际上就是依次强制割一条边之后跑最大流,关于如何强制割一条边的话就需要我们先退流然后直接钦定流量上界为 \(0\) 即可。

#include<bits/stdc++.h>
using namespace std;
const int N=7e2+5;
const int INF=1e9+7;
int n,f[N],len=0,res,sum=0;vector<int> opt;
struct Num{int a,b,c,id;}s[N];
bool cmp(Num a,Num b){return a.c<b.c;}
int from,to,tot,id[N][2],ID[N];
struct Edge{int nxt,to,flow;}e[N*N*2];int fir[N*2],e_size=1;
void add(int u,int v,int w){e[++e_size]=(Edge){fir[u],v,w},fir[u]=e_size;}
int cur[N*2],dis[N*2],q[N*2],head,tail;bool vis[N*2];
bool bfs(int s,int t){
	for(int i=1;i<=tot;++i) cur[i]=fir[i],dis[i]=INF;
	dis[s]=0,head=1,tail=0,q[++tail]=s;
	while(head<=tail){
		int u=q[head++];
		for(int i=fir[u];i;i=e[i].nxt){
			int v=e[i].to;
			if(!e[i].flow||dis[v]<=dis[u]+1) continue;
			dis[v]=dis[u]+1,q[++tail]=v;if(v==t) return true;
		}
	}
	return false;
}
int dfs(int u,int t,int flow){
	if(u==t) return flow;
	int res=0;vis[u]=true;
	for(int i=cur[u];i&&flow;i=e[i].nxt){
		int v=e[i].to;cur[u]=i;
		if(vis[v]||!e[i].flow||dis[v]!=dis[u]+1) continue;
		int tmp=dfs(v,t,min(flow,e[i].flow));
		e[i].flow-=tmp,e[i^1].flow+=tmp,flow-=tmp,res+=tmp;
	}
	return vis[u]=false,res;
}
int dinic(int s,int t,int flow){
	int res=0;
	while(flow&&bfs(s,t)){
		int tmp=dfs(s,t,flow);
		flow-=tmp,res+=tmp;
	}
	return res;
}
int solve(){
	cin>>n,len=0,sum=0;
	for(int i=1;i<=n;++i) scanf("%d",&s[i].a);
	for(int i=1;i<=n;++i) scanf("%d",&s[i].b);
	for(int i=1;i<=n;++i) scanf("%d",&s[i].c);
	for(int i=1;i<=n;++i) s[i].id=i;
	for(int i=1;i<=n;++i){
		f[i]=1;
		for(int j=1;j<i;++j) if(s[j].a<s[i].a)
			f[i]=max(f[i],f[j]+1);
		len=max(len,f[i]);
	}
	for(int i=1;i<=tot;++i) fir[i]=0;
	tot=0,e_size=1,from=++tot,to=++tot;
	for(int i=1;i<=n;++i){
		id[i][0]=++tot,id[i][1]=++tot;
		add(id[i][0],id[i][1],s[i].b);
		ID[i]=e_size,add(id[i][1],id[i][0],0);
	}
	for(int i=1;i<=n;++i) if(f[i]==1)
		add(from,id[i][0],INF),add(id[i][0],from,0);
	for(int i=1;i<=n;++i) if(f[i]==len)
		add(id[i][1],to,INF),add(to,id[i][1],0);
	for(int i=1;i<=n;++i){
		for(int j=1;j<i;++j) if(f[j]+1==f[i]&&s[j].a<s[i].a)
		add(id[j][1],id[i][0],INF),add(id[i][0],id[j][1],0);
	}
	sum+=dinic(from,to,INF),printf("%d ",res=sum);
	sort(s+1,s+1+n,cmp),opt.clear();
	for(int i=1;i<=n;++i){
		int tmp=e[ID[s[i].id]^1].flow;sum-=tmp;
		dinic(to,e[ID[s[i].id]].to,tmp);
		dinic(e[ID[s[i].id]^1].to,from,tmp);
		e[ID[s[i].id]].flow=0,sum+=dinic(from,to,INF);
		if(sum+s[i].b==res) res=sum,opt.push_back(s[i].id);
		else e[ID[s[i].id]].flow=INF,sum+=dinic(from,to,INF);
	}
	sort(opt.begin(),opt.end()),printf("%d\n",(int)opt.size());
	for(int i=0;i<(int)opt.size();++i) printf("%d ",opt[i]);
	return printf("\n"),0;
}
int main(){
	int T;cin>>T;while(T--) solve();
	return 0;
}
posted @ 2021-12-15 18:12  Point_King  阅读(41)  评论(0)    收藏  举报