「清华集训 2017」我的生命已如风中残烛

题目描述:

链接

思路:

(小声:离线赛遇到这个大样例真的自闭)

暴力思路就是往下找下一个会换到的钉子,然后只到没有可以达到的钉子为止。

然后可以发现会出现围着几个钉子绕圈的情况,那就可以直接算出在这一段上绕的次数。(根据官方题解所说最多只会出现\(log L\)次循环,因为每次长度会减少至少一半)

这样复杂度就是\(O(n^2mlogL)\),60pts (60分的思路考试被自己打成了20分,给的大样例仿佛没有)

可以发现复杂度就是在寻找下一个钉子上,这时候可以考虑预处理,对于每个钉子\(i\),记录下其他钉子与它的连线和\(x\)轴正方向的极角,按极角排序(如果是负数的时候直接加上2\(\pi\)

这样找的时候就可以直接二分然后往下找(不过这样其实复杂度上限也没变,但是可以过……)

//「清华集训 2017」我的生命已如风中残烛
#include<bits/stdc++.h>
#define M 505
#define db long double
#define ll long long
using namespace std;
void Rd(ll &res) {
	res=0;
	char c;
	int fl=1;
	while(c=getchar(),c<48)if(c=='-')fl=-1;
	do res=(res<<1)+(res<<3)+(c^48);
	while(c=getchar(),c>=48);
	res*=fl;
}
int T,n,m;
struct Point {
#define P Point
	ll x,y;
	P operator+(const P&_)const {
		return (P)<%x+_.x,y+_.y%>;
	}
	P operator-(const P&_)const {
		return (P)<%x-_.x,y-_.y%>;
	}
	ll operator*(const P&_)const {
		return x*_.x+y*_.y;
	}
	ll operator^(const P&_)const {
		return x*_.y-_.x*y;
	}
} a[M];
const db eps=1e-15;
const db pi=acos(-1);
db calc(P A,P B) {
	return sqrt(1.0*(A.x-B.x)*(A.x-B.x)+1.0*(A.y-B.y)*(A.y-B.y));
}
struct Node {
	int id;
	db l,ang;
	bool operator<(const Node&_)const {
		if(abs(ang-_.ang)<=eps)return l>_.l;
		return ang>_.ang;
	}
} A[M],To[M][M];
struct P1 {
#define N 20000005
	int stk[N];
	db Len[N],len,pl;
	bool vis[M];
	void add(int id,int y,P L,P R) {
		P x=R-L;
		db ang=atan2(x.y,x.x);
		if(ang<0)ang+=2*pi;
		To[id][y]=(Node)<%y,calc(L,R),ang%>;
	}
	void solve() {
		P s,t;
		ll l,ans;
		for(int i=1; i<=n; i++) {
			for(int j=1; j<=n; j++)add(i,j,a[i],a[j]);
			sort(To[i]+1,To[i]+n+1);
		}
		while(m--) {
			Rd(s.x),Rd(s.y),Rd(t.x),Rd(t.y),Rd(l);
			for(int i=1; i<=n; i++)add(0,i,s,a[i]);
			sort(To[0]+1,To[0]+n+1);
			int r=0,la=0,L=1,R=0,id=0;
			len=1.0*l,ans=1,Len[0]=0;
			for(int i=1; i<=n; i++)vis[i]=0;
			while(1) {
//				printf("%d ",la);
				P now=t-s;
				db ang=atan2(now.y,now.x),l=len;
				if(ang<0)ang+=2*pi;
				int x=-1,p=lower_bound(To[la]+1,To[la]+n+1,(Node)<%1,l,ang%>)-To[la];
				if(p>n)p=1;
				for(int i=1; i<=n; i++) {
					if(To[la][p].id==la||abs(To[la][p].ang-ang)<=eps);
					else if(To[la][p].l<=len) {
						x=To[la][p].id;
						break;
					}
					p++;
					if(p==n+1)p=1;
				}
				if(x==-1)break;
				pl=calc(s,a[x]),ans++,len-=pl;
				if(ans>3) {
					if(vis[x]) {
						while((L<=R)&&stk[L]!=x)vis[stk[L]]=0,L++;//找到上一次这个数出现的位置
						ll t=R-L+1,k=len/(Len[R]-Len[L]+pl);
						ans+=1ll*t*k,len-=k*(Len[R]-Len[L]+pl);
						for(int i=L+1; i<=R; i++)vis[stk[i]]=0;
						R=L,Len[L]=0;
					} else vis[x]=1,stk[++R]=x,Len[R]=Len[R-1]+pl;
				}
				t=(P)<%a[x].x*2-s.x,a[x].y*2-s.y%>,s=a[x],la=x;
			}
			printf("%lld\n",ans);
		}
	}
} p1;
int main() {
	freopen("life.in","r",stdin);
	freopen("life.out","w",stdout);
	scanf("%d",&T);
	while(T--) {
		scanf("%d%d",&n,&m);
		for(int i=1; i<=n; i++)Rd(a[i].x),Rd(a[i].y);
		p1.solve();
	}
	return 0;
}
posted @ 2020-05-24 16:59  季芊月  阅读(186)  评论(0编辑  收藏  举报