CF 1408H - Rainbow Triples

题目链接

首先可以考虑二分答案。首先不难发现的是一定有一组解不会有两组三元组满足: \(i_1\le i_2\le k_2\le i_1\)

\(i,k\) 显然就是选最先和最后的几个 \(0\)。考虑每个数字连的边,会发现如果在左边他会连向一段前缀,如果在右边会连向一段后缀。

那么考虑霍尔定理,不难发现选择的点一定是一段区间。于是显然可以考虑扫描线过去求解,复杂度 \(O(n\log ^2)\),并不足以通过。

但是仔细考虑这个线段树,会发现的是实际上线段树上的修改与查询都是一样的(或不会对答案造成影响),于是只需要做一次扫描线,复杂度\(O(n\log )\)

代码

#include<bits/stdc++.h>
using namespace std;

const int N = 5e5+5;
int n, a[N];

vector<int> zeros;

int lft[N], rgt[N];

#define mid ((l+r)>>1)	

int tg[N<<2], mx[N<<2];
inline void pushdown(int x,int l,int r){
	if(tg[x]!=0)if(l^r){
		mx[x<<1]+=tg[x],mx[x<<1|1]+=tg[x];
		tg[x<<1]+=tg[x],tg[x<<1|1]+=tg[x];
	}
	tg[x] = 0;
}
inline void modify(int x,int l,int r,int ql,int qr,int s){
	pushdown(x,l,r);
	if(ql<=l&&qr>=r){
		tg[x]+=s,mx[x]+=s;return;
	}
	if(ql<=mid)modify(x<<1,l,mid,ql,qr,s);
	if(qr>mid)modify(x<<1|1,mid+1,r,ql,qr,s);
	mx[x] = max(mx[x<<1],mx[x<<1|1]);
}
inline void build(int x,int l,int r){
	tg[x] = 0;
	if(l==r){mx[x] = -n-l;return ;}
	build(x<<1,l,mid),build(x<<1|1,mid+1,r);
	mx[x] = max(mx[x<<1], mx[x<<1|1]);
}
#undef mid

inline void Main(){
	cin >> n;
	zeros.clear();
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		if(a[i]==0)zeros.push_back(i);
	}
	for(int i=1;i<=n;i++)lft[i] = rgt[i] = 0;
	int midpoint = zeros.size()?zeros[zeros.size() / 2]:1;
	for(int i=1,P=0;i<=midpoint;i++)
		if(!a[i])++P;
		else lft[a[i]]=P;
	for(int i=n,P=0;i>midpoint;i--)
		if(!a[i])++P;
		else rgt[a[i]]=P;
	vector<vector<int> > opt(n);
	build(1,0,n);
	for(int i=1;i<=n;i++){
		opt[rgt[i]].push_back(lft[i]);
	}
	for(int r=n-1;~r;--r){
		for(auto l: opt[r]){
			modify(1,0,n,l,n,1);
		}
	}
	int maxm = zeros.size()/2;
    for(int r=n-1;~r;--r){
		int mm = mx[1];
		maxm = min(maxm, -mm + r);
		for(auto l: opt[r]){
			modify(1,0,n,l,n,-1);
		}
	}
	cout << maxm << endl;
}

int main()
{
	int T;cin >> T;
	while(T--){
		Main();
	}
}
posted @ 2020-10-04 10:49  jerome_wei  阅读(224)  评论(0编辑  收藏  举报