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;
}
静渊以有谋,疏通而知事。