「清华集训 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;
}