CF1408H Rainbow Triples

一、题目

点此看题

当你 \(\tt Wa\) 了十几发之后,评测机都会嘲笑你,\(\tt wdnmd\),以后还是要写注释以免写错关键细节:

二、解法

直接考虑怎么建网络流模型,但是这题是两个点决定一个点(两个 \(0\) "匹配"中间一个权值),这个关系不太好建。考虑拆分,首先观察到我们按 \(0\) 数量平均分成两段 \(L,R\),那么显然不会出现两段内部匹配一个权值,否则我们可以通过调整使之跨过中线,并且这样更优。

那么可以转成一个点决定一个点,也就是我们只和 \(L,R\) 中的点匹配,然后和 \(0\) 的个数除 \(2\)\(\min\) 即可。设 \(l_x\) 为颜色 \(x\) 出现在 \(L\) 最右端的位置,\(r_x\) 为颜色 \(x\) 出现在 \(R\) 最左端的位置:

可以手算最小割,发现可能被割的边只可能是 源点与权值的边 和 零点与汇点的边。可以套路地枚举一些东西,我们枚举左边断掉的零点前缀,维护所有右边断掉的零点后缀的答案

考虑对于对于一种权值,如果当前左边和汇点不连通,那么考虑右边的一段前缀就不需要割掉这个权值了,所以可以拿一棵线段树来维护,只需要区间修改和全局查询,时间复杂度 \(O(n\log n)\)

三、总结

网络流中我们常常表示单点,单点对单点的关系,而难以简单地表达多点之间的一种关系。虽然有时候可以利用图上的其他意义(比如路径),但如果要求简单建图,我们这时候可以考虑拆分成单点和单点之间的关系。

手算最小割常常和贪心法、枚举法、简单数据结构挂钩,很多题的计算方法都类似。

#include <cstdio>
#include <iostream>
using namespace std;
const int M = 500005;
const int inf = 0x3f3f3f3f;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int T,n,m,k,a[M],ty[M],pz[M],lc[M],rc[M];
int nxt[M],b[M],mi[4*M],fl[4*M];
void down(int i)
{
	if(!fl[i]) return ;
	fl[i<<1]+=fl[i];mi[i<<1]+=fl[i];
	fl[i<<1|1]+=fl[i];mi[i<<1|1]+=fl[i];
	fl[i]=0;
}
void build(int i,int l,int r)
{
	fl[i]=0;
	if(l==r) {mi[i]=b[l];return ;}
	int mid=(l+r)>>1;
	build(i<<1,l,mid);
	build(i<<1|1,mid+1,r);
	mi[i]=min(mi[i<<1],mi[i<<1|1]);
}
void add(int i,int l,int r,int L,int R)
{
	if(L>r || l>R) return ;
	if(L<=l && r<=R) {mi[i]--;fl[i]--;return ;}
	int mid=(l+r)>>1;down(i);
	add(i<<1,l,mid,L,R);
	add(i<<1|1,mid+1,r,L,R);
	mi[i]=min(mi[i<<1],mi[i<<1|1]);
}
void work()
{
	n=read();
	for(int i=1;i<=n;i++)
		lc[i]=rc[i]=-1,b[i]=inf;
	for(int i=1;i<=n;i++)
		a[i]=read(),pz[i]=pz[i-1]+!a[i];
	m=pz[n];k=0;nxt[n+1]=n+1;
	for(int i=1;i<=n;i++)
	{
		ty[i]=pz[i]<=(m/2)?1:2;
		if(a[i] && ty[i]==1) lc[a[i]]=i;
	}
	for(int i=n;i>=1;i--)
	{
		if(a[i] && ty[i]==2) rc[a[i]]=i;
		if(!a[i]) nxt[i]=i;else nxt[i]=nxt[i+1];
		//find the next zero-cut
	}
	for(int i=1;i<=n;i++)
		if(lc[i]!=-1 || rc[i]!=-1) k++;
	for(int i=1;i<=n;i++)
		if(!a[i] && ty[i]==2) b[i]=m-pz[i]+1+k;
	b[n+1]=k;build(1,1,n+1);
	for(int i=1;i<=n;i++)//for every color!!!!
		if(lc[i]==-1 && rc[i]!=-1)
			add(1,1,n+1,1,nxt[rc[i]]);
	int ans=min(m>>1,mi[1]);
	for(int nw=0,i=1;i<=n;i++)
	{
		if(!a[i]) nw++;
		if(a[i] && lc[a[i]]==i)
		{
			if(rc[a[i]]==-1) add(1,1,n+1,1,n+1);
			else add(1,1,n+1,1,nxt[rc[a[i]]]);
		}
		ans=min(ans,nw+mi[1]);
	}
	printf("%d\n",ans);
}
signed main()
{
	T=read();
	while(T--) work();
}
posted @ 2021-11-04 21:41  C202044zxy  阅读(104)  评论(0编辑  收藏  举报