zoukankan      html  css  js  c++  java
  • Gym102576D Clique

    Clique

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

    (n ≤ 3000)

    题解

    王修涵《动态规划》。

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

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

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

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

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

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

    时间复杂度(O(n^2log 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,'
    ');
    }
    int main(){
    	for(int t=read<int>();t--;) real_main();
    	return 0;
    }
    
  • 相关阅读:
    Android LBS系列06 位置策略(二)模拟位置数据的方法
    Android LBS系列03 Geocoder类与地址显示
    Java 容器集合框架概览
    Android 按钮类控件大集锦:Button ToggleButton CheckBox RadioButton
    Java中的包与导入
    《Head First设计模式》 读书笔记01 策略模式
    Android Fragment和Activity
    Java中的final
    《Head First设计模式》 读书笔记07 封装调用:命令模式
    Google Maps Android API V2的使用及问题解决
  • 原文地址:https://www.cnblogs.com/autoint/p/13393763.html
Copyright © 2011-2022 走看看