Gym102576D Clique

Clique

给一个圆的\(n\)条圆弧,选出一个尽量大的子集使得其中的圆弧两两有交。

\(n ≤ 3000\)

题解

王修涵《动态规划》。

枚举一条必须选的圆弧,使得不存在其他选了的圆弧被它完全包含。

这个策略简单来说就是枚举最小的线段。

对于与它两端都有交的圆弧,选入不会影响答案;对于与它不相交的圆弧,显然不能选。

剩下与它左端点相交的圆弧和右端点相交的圆弧,两边的圆弧可以从两个方向接上。

转化为二维平面上的问题:选出若干黑点和白点使得不存在黑点严格在白点的左下方。线段树优化DP即可。

在代码里面,若\(x_1< x_0,y_1< y_0\)就不合法。把线段树下标设成\(\max y_0\)就可以DP了。

时间复杂度\(O(n^2\log n)\)

CO int N=3e3+10,L=1e6;
int tree[4*N],tag[4*N];

#define lc (x<<1)
#define rc (x<<1|1)
#define mid ((l+r)>>1)
IN void put_tag(int x,int v){
	tree[x]+=v,tag[x]+=v;
}
IN void push_down(int x){
	if(tag[x]){
		put_tag(lc,tag[x]),put_tag(rc,tag[x]);
		tag[x]=0;
	}
}
void build(int x,int l,int r){
	tree[x]=tag[x]=0;
	if(l==r) return;
	build(lc,l,mid),build(rc,mid+1,r);
}
void insert(int x,int l,int r,int p,int v){
	if(l==r) {tree[x]=v; return;}
	push_down(x);
	if(p<=mid) insert(lc,l,mid,p,v);
	else insert(rc,mid+1,r,p,v);
	tree[x]=max(tree[lc],tree[rc]);
}
void modify(int x,int l,int r,int ql,int qr,int v){
	if(ql<=l and r<=qr) return put_tag(x,v);
	push_down(x);
	if(ql<=mid) modify(lc,l,mid,ql,qr,v);
	if(qr>mid) modify(rc,mid+1,r,ql,qr,v);
	tree[x]=max(tree[lc],tree[rc]);
}
int query(int x,int l,int r,int ql,int qr){
	if(ql<=l and r<=qr) return tree[x];
	push_down(x);
	if(qr<=mid) return query(lc,l,mid,ql,qr);
	if(ql>mid) return query(rc,mid+1,r,ql,qr);
	return max(query(lc,l,mid,ql,qr),query(rc,mid+1,r,ql,qr));
}
#undef lc
#undef rc
#undef mid

int a[N],b[N];

void real_main(){
	int n=read<int>();
	for(int i=1;i<=n;++i) read(a[i]),read(b[i]);
	int ans=0;
	for(int w=1;w<=n;++w){
		vector<tuple<int,int,int> > p;
		int already=1;
		for(int i=1;i<=n;++i)if(i!=w){
			bool l=0,r=0;
			if(a[i]<=b[i]){
				if(a[i]<=a[w] and a[w]<=b[i]) l=1;
				if(a[i]<=b[w] and b[w]<=b[i]) r=1;
			}
			else{
				if(a[i]<=a[w] or a[w]<=b[i]) l=1;
				if(a[i]<=b[w] or b[w]<=b[i]) r=1;
			}
			if(l and r) ++already;
			else if(l){ // use a[w] as origin
				int x=(b[i]+L-a[w])%L;
				int y=(a[w]+L-a[i])%L;
				p.emplace_back(x,1,y);
			}
			else if(r){
				int x=(a[i]+L-a[w])%L;
				int y=(a[w]+L-b[i])%L;
				p.emplace_back(x,0,y);
			} // invalid if x1<x0 and y1<y0
		}
		if(p.size()){
			sort(p.begin(),p.end());
			vector<int> d;
			for(CO tuple<int,int,int>&t:p) d.push_back(get<2>(t));
			sort(d.begin(),d.end());
			d.erase(unique(d.begin(),d.end()),d.end());
			build(1,1,d.size());
			for(tuple<int,int,int>&t:p)
				get<2>(t)=lower_bound(d.begin(),d.end(),get<2>(t))-d.begin()+1;
			for(CO tuple<int,int,int>&t:p){
				int y=get<2>(t);
				if(get<1>(t)){
					if(1<=y-1) modify(1,1,d.size(),1,y-1,1);
					insert(1,1,d.size(),y,query(1,1,d.size(),y,d.size())+1);
				}
				else modify(1,1,d.size(),y,d.size(),1);
			}
			ans=max(ans,tree[1]+already);
		}
		else ans=max(ans,already);
	}
	write(ans,'\n');
}
int main(){
	for(int t=read<int>();t--;) real_main();
	return 0;
}

posted on 2020-07-28 20:48  autoint  阅读(220)  评论(0编辑  收藏  举报

导航