【校內模擬】Kingdom(DP)

由於太過SB,懶得寫簡要題意了。。。


題解:

不難發現我們可以直接考慮 ii 能不能直接走到 jj 然後 O(n2)O(n^2) DP即可。

最開始把題目看錯成距離直線距離不超過 dd 了,我Splay維護動態凸包都寫完了艹。。

線段的話也是非常簡單,考慮一個不在圓 ii 內部的點,它顯然會限制 ii 向後連出去的點的極角範圍。

於是維護這個範圍即可,然而我的維護方式過於SB導致寫掛了,感覺std的方法還挺好的,這裏記一下。

容易注意到 ii 允許向後連出的範圍是所有點限制出的範圍的交,並且每個點單獨造成的限制弧度嚴格小於 π\pi

維護一個基準向量,記錄 l,rl,r 表示我們允許 ii 連出去的邊的極角在基準向量 +[l,r]+[l,r] 的弧度範圍中,這樣維護比較簡單。


代碼:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define db double
#define cs const

namespace IO{

inline char gc(){
	static cs int Rlen=1<<22|1;static char buf[Rlen],*p1,*p2;
	return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++; 
}template<typename T>T get_integer(){
	char c;bool f=false;while(!isdigit(c=gc()))f=c=='-';T x=c^48;
	while(isdigit(c=gc()))x=((x+(x<<2))<<1)+(c^48);return f?-x:x;
}inline int gi(){return get_integer<int>();}

}using namespace IO;

using std::cerr;
using std::cout;

cs int N=2e3+7;
cs db eps=1e-7,PI=acos(-1);

struct Pnt{
	db x,y;Pnt(){}Pnt(db _x,db _y):x(_x),y(_y){}
	friend Pnt operator+(cs Pnt &a,cs Pnt &b){return Pnt(a.x+b.x,a.y+b.y);}
	friend Pnt operator-(cs Pnt &a,cs Pnt &b){return Pnt(a.x-b.x,a.y-b.y);}
	friend db operator*(cs Pnt &a,cs Pnt &b){return a.x*b.y-b.x*a.y;}
	friend db dot(cs Pnt &a,cs Pnt &b){return a.x*b.x+a.y*b.y;}
	db len()cs{return sqrt(x*x+y*y);}
	db ang()cs{return atan2(y,x);}
};

int n,d;

Pnt p[N];

int dp[N];

struct rng{double l,r,dir;};
rng pr[N][N],sf[N][N];

double adj(double x){
	while(x<-PI-eps)x+=2*PI;
	while(x>PI+eps)x-=2*PI;
	return x;
}

rng calc(cs Pnt &p,cs Pnt &q){
	db dlt=adj(asin(d/(p-q).len()));
	db rad=atan2(q.y-p.y,q.x-p.x);
	return {-dlt,dlt,rad};
}

rng merge(rng a,rng b){
	if(a.dir>PI+PI)return b;
	if(b.dir>PI+PI)return a;
	db dlt=adj(b.dir-a.dir);
	db l=b.l+dlt,r=b.r+dlt;
	a.l=std::max(a.l,l);
	a.r=std::min(a.r,r);
	return a;
}

bool in(cs rng &a,double rad){
	if(a.dir>PI+PI)return true;
	rad=adj(rad-a.dir);
	return a.l-eps<rad&&rad<a.r+eps;
}

bool judge(int i,int j){
	return in(pr[i][j+1],(p[j]-p[i]).ang())&&
		in(sf[j][i-1],(p[i]-p[j]).ang());
}

void Main(){
	n=gi(),d=gi();
	for(int re i=1;i<=n;++i)
		p[i].x=gi(),p[i].y=gi();
	for(int re i=1;i<=n;++i){
		sf[i][i]=(rng){0,PI+PI,1e9};
		pr[i][i]=(rng){0,PI+PI,1e9};
		for(int re j=i+1;j<=n;++j){
			if((p[i]-p[j]).len()<d+eps)
				sf[i][j]=sf[i][j-1];
			else 
				sf[i][j]=merge(sf[i][j-1],calc(p[i],p[j]));
		}
		for(int re j=i-1;j;--j){
			if((p[i]-p[j]).len()<d+eps)
				pr[i][j]=pr[i][j+1];
			else
				pr[i][j]=merge(pr[i][j+1],calc(p[i],p[j]));
		}
	}dp[1]=1;
	for(int re i=2;i<=n;++i){
		dp[i]=1e9;
		for(int re j=1;j<i;++j)
			if(judge(i,j))dp[i]=std::min(dp[i],dp[j]+1);
	}
	cout<<dp[n]<<"\n";
}

inline void file(){
#ifdef zxyoi
	freopen("kingdom.in","r",stdin);
#else
#ifndef ONLINE_JUDGE
	freopen("kingdom.in","r",stdin);
	freopen("kingdom.out","w",stdout);
#endif
#endif
}signed main(){file();Main();return 0;}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章