SDOI2014 LIS

题目链接:戳我

我们先做一个DP,就能求出来到前i位的最长上升子序列的长度(maxx[i]数组)。

然后我们考虑求最小割——给每个点拆点,如果要割掉这个点,就相当于把in[i]--out[i]这条边给割掉了。

然后如果在最长上升子序列中,该位下面可以接很多,那么就从该位代表的out向下面可以接的in连边。

然后跑最小割就行了QAQ

但是......之后还需要求字典序最小的最小割方案。
那么我们先把边从小到大排个序,然后把所有可能割边都捞出来,贪心地选择。
但是有一个问题,就是我们选择了一个边之后,它会对我们之后的决策产生影响(比如说,本来应该选的边可以不选了)
所以我们要强行退流(具体操作可以看代码)。我们知道选了这条边之后,这条边的可替代品就不需要选了,这些可替代它的边显然是存在于S-u,v-T之间的。所以我们直接退掉他们满流边的流量就行啦!

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define S 0
#define T 2*n+1
#define MAXN 1510
#define INF 0x3f3f3f3f
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch==-1)f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int TT,n,t=1,cnt,kkk;
int num[MAXN];
int ans[MAXN],in[MAXN],out[MAXN],done[MAXN];
int head[MAXN],cur[MAXN],dis[MAXN],dp[MAXN][MAXN],maxx[MAXN];
struct Node{int id,a,b,c;}node[MAXN];
struct Edge{int nxt,to,dis;}edge[MAXN*MAXN*2];
inline void add(int from,int to,int dis)
{
//	printf("[%d %d] %d\n",from,to,dis);
	edge[++t].nxt=head[from],edge[t].to=to,edge[t].dis=dis,head[from]=t;
	edge[++t].nxt=head[to],edge[t].to=from,edge[t].dis=0,head[to]=t;
}
inline bool check(int s,int t)
{
	queue<int>q;
	memset(done,0,sizeof(done));
	q.push(s);done[s]=1;
	while(!q.empty())
	{
		int u=q.front();q.pop();
		if(u==t) return true;
		for(int i=head[u];i;i=edge[i].nxt)
		{
			int v=edge[i].to;
			if(edge[i].dis&&done[v]==0) 
				q.push(v),done[v]=1;
		}
	}
	return false;
}
inline bool bfs(int s,int t)
{
	queue<int>q;
	memset(dis,0x3f,sizeof(dis));
	memcpy(cur,head,sizeof(head));
	q.push(s);dis[s]=0;
	while(!q.empty())
	{
		int u=q.front(); q.pop();
		for(int i=head[u];i;i=edge[i].nxt)
		{
			int v=edge[i].to;
			if(edge[i].dis&&dis[v]==0x3f3f3f3f)
			{
				dis[v]=dis[u]+1;
				q.push(v);
			}
		}
	}
	if(dis[t]==0x3f3f3f3f) return false;
	return true;
}
inline int dfs(int x,int t,int f)
{
	if(!f||x==t) return f;
	int used=0,w;
	for(int i=cur[x];i;i=edge[i].nxt)
	{
		int v=edge[i].to;
		cur[x]=i;
		if(dis[v]==dis[x]+1&&(w=dfs(v,t,min(edge[i].dis,f))))
		{
			edge[i].dis-=w;
			edge[i^1].dis+=w;
			used+=w,f-=w;
			if(!f) break;
		}
	}
	return used;
}
inline int dinic(int s,int t)
{
	int cur_ans=0;
	while(bfs(s,t)) cur_ans+=dfs(s,t,INF);
	return cur_ans;
}
inline void pre()
{
	for(int i=1;i<=n;i++)
	{
		maxx[i]=1;
		for(int j=1;j<i;j++)
			if(node[i].a>node[j].a)
				maxx[i]=max(maxx[i],maxx[j]+1);
	}	
	for(int i=1;i<=n;i++) kkk=max(kkk,maxx[i]);
}
inline void add_edge()
{
	for(int i=1;i<=n;i++) in[i]=i;
	for(int i=n+1;i<=2*n;i++) out[i-n]=i;
	for(int i=1;i<=n;i++) add(in[i],out[i],node[i].b),num[i]=t-1;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<i;j++)
		{
			if(maxx[j]+1==maxx[i]&&node[j].a<node[i].a)
				add(out[j],in[i],INF);
		}
	}
	for(int i=1;i<=n;i++) if(maxx[i]==1) add(S,in[i],INF);
	for(int i=1;i<=n;i++) if(maxx[i]==kkk) add(out[i],T,INF);
}
inline bool cmp(struct Node x,struct Node y){return x.c<y.c;}
inline void solve()
{
	for(int i=1;i<=n;i++)
	{
		int x=node[i].id;
		if(check(in[x],out[x])) continue;
		ans[++cnt]=x;
		while(bfs(T,out[x])) dfs(T,out[x],INF);
		while(bfs(in[x],S)) dfs(in[x],S,INF);
		edge[num[x]].dis=0,edge[num[x]^1].dis=0;
	}
}
int main()
{
	#ifndef ONLINE_JUDGE
	freopen("ce.in","r",stdin);
	#endif
	TT=read();
	while(TT--)
	{
		memset(head,0,sizeof(head));
		cnt=0,kkk=0;
		t=1;
		n=read();
		for(int i=1;i<=n;i++) node[i].a=read();
		for(int i=1;i<=n;i++) node[i].b=read();
		for(int i=1;i<=n;i++) node[i].c=read();
		pre();
		add_edge();
		printf("%d ",dinic(S,T));
		for(int i=1;i<=n;i++) node[i].id=i;
		sort(&node[1],&node[n+1],cmp);
		solve();
		printf("%d\n",cnt);
		sort(&ans[1],&ans[cnt+1]);
		for(int i=1;i<=cnt;i++) printf("%d ",ans[i]); puts("");
	}
	return 0;
}
posted @ 2019-06-15 14:07  风浔凌  阅读(167)  评论(0编辑  收藏  举报