51NOD 1467 旋转绳 二分查找 模拟

本文针对CodeForces上的旋转绳问题,提供了一种基于二分查找的解决方案。问题涉及平面上的钉子和绕其旋转的绳子,通过优化算法确定绳子最终缠绕的钉子。适用于算法竞赛和数据结构学习。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目来源:  CodeForces
基准时间限制:1.5 秒 空间限制:131072 KB 分值: 80  难度:5级算法题
 收藏
 关注

平面上有n个钉子,他们从1到n编号,第i个钉子的坐标是 (xi, 0)。然后我们我们把一个长度为L,带重物的绳子系到第i个钉子上(那么重物所在的坐标是(xi, -L))。然后用力将重物向右推,开始逆时针旋转。同时,如果旋转的过程中碰到其它的钉子,就会绕着那个钉子旋转。假设每个钉子都很细,重物绕着它旋转时,不影响到绳子的长度。


更一般的,如果绳子碰到多个钉子,那么它会绕着最远的那个钉子转。特殊的,如果绳子的末端碰到了一个钉子,那么也会绕着那个钉子以长度为0的绳子在转。

经过一段时间之后,重物就会一直绕着某个钉子转。

现在有m个查询,每个查询给出初始的绳子长度以及挂在哪个钉子下旋转,请找出重物最终会绕哪个钉子旋转。

样例解释:


Input
单组测试数据。
第一行包含两个整数n 和 m (1 ≤ n, m ≤ 2*10^5),表示钉子的数目以及查询的数目。
接下来一行包含n个整数 x1, x2, ..., xn ( -10^9 ≤ xi ≤ 10^9),表示每个钉子的坐标。保证输入的钉子的坐标两两不相同。
接下来m行给出查询。每行给出ai (1 ≤ ai ≤ n) 和 li(1 ≤ li ≤ 10^9),表示该查询的重物挂在第ai个钉子上,绳子长度是li。
Output
输出m行,第i行输出第i个查询的重物最终绕着哪个钉子转。
二分查找钉子然后加了点优化..
 
  
 
  
#include<iostream>
#include<algorithm>
#include<stdio.h>
using namespace std;
int ok(long long x);
struct node
{
	long long data;
	int id;
};
bool cmp(node x,node y)
{
	return x.data<y.data;
}
node s[200003];
long long s1[200003]={0};
int n=0,m=0;
int jilv[3]={0};
int t=0; 
int main()
{
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		for(int i=1;i<=n;i++)
		 {
		 	scanf("%lld",&s[i].data);
		 	s[i].id=i;
		 	s1[i]=s[i].data;
		 }
		sort(s+1,s+n+1,cmp);
		t=0;
		while(m--)
		{
			int x=0,r=0;
			t=0;
			cin>>x>>r;
			x=ok(s1[x]);
			int end=0;
			int sum=0;
			jilv[t++]=x;
			if(s[x].data+r<s[x+1].data||x==n) sum++;
            while(1)
            {
            	int y=0;
               if(sum%2==0)
			   {
			   	   y=ok(s[x].data+r);
			   	   if(s[x].data+r==s[y].data)
			   	   {
			   	   	  end=y;
			   	   	  break;
				   }
				   else if(s[x].data==s[y].data)
				   {
				   	  end=y;
				   	  break;
				   }
				   else
				   {
				   	  r=r-(s[y].data-s[x].data);
				   	  x=y;
				   }
				   sum++;
				   if(t<2)
				   {
				   	jilv[t++]=x;
				   }
				   else
				   {
				   	  if(jilv[0]==x)
				   	  {
				   	  	 int R=abs(s[jilv[0]].data-s[jilv[1]].data);
				   	  	 int p=r/(R);
				   	  	 if(p&1)
				   	  	 {
				   	  	 	r=r-p*R;
				   	  	 	x=jilv[1];
				   	  	 	jilv[0]=x;
				   	  	 	t=1;
				   	  	 	sum++;
						 }
						 else
						 {
						 	r=r-p*R;
						 	t=1;
						 }
					  }
					  else
					  {
					  	jilv[0]=jilv[1];
					  	jilv[1]=x;
					  }
				   }
			   }
			   else
			   {   
					y=ok(s[x].data-r);
					if(y==0)
					{
				         r=r-(s[x].data-s[1].data);
				         x=1;
				         sum++;
				    if(t<2)
				   {
				   	jilv[t++]=x;
				   }
				   else
				   {
				   	  if(jilv[0]==x)
				   	  {
				   	  	 int R=abs(s[jilv[0]].data-s[jilv[1]].data);
				   	  	 int p=r/(R);
				   	  	 if(p&1)
				   	  	 {
				   	  	 	r=r-p*R;
				   	  	 	x=jilv[1];
				   	  	 	jilv[0]=x;
				   	  	 	t=1;
				   	  	 	sum++;
						 }
						 else
						 {
						 	r=r-p*R;
						 	t=1;
						 }
					  }
					  else
					  {
					  	jilv[0]=jilv[1];
					  	jilv[1]=x;
					  }
				   }
				         continue;
					}
					if(s[x].data-r==s[y].data)
					{
						end=y;
						break;
					}
					else if(s[x].data==s[y+1].data)
					{
						end=x;
						break;
					}
					else
					{
						r=r-(s[x].data-s[y+1].data);
						x=y+1;
					}
					sum++;
				   if(t<2)
				   {
				   	jilv[t++]=x;
				   }
				   else
				   {
				   	  if(jilv[0]==x)
				   	  {
				   	  	 int R=abs(s[jilv[0]].data-s[jilv[1]].data);
				   	  	 int p=r/(R);
				   	  	 if(p&1)
				   	  	 {
				   	  	 	r=r-p*R;
				   	  	 	x=jilv[1];
				   	  	 	jilv[0]=x;
				   	  	 	t=1;
				   	  	 	sum++;
						 }
						 else
						 {
						 	r=r-p*R;
						 	t=1;
						 }
					  }
					  else
					  {
					  	jilv[0]=jilv[1];
					  	jilv[1]=x;
					  }
				   }
			   }
			}
			printf("%d\n",s[end].id);
		}
	}
	return 0;
}
int ok(long long x)
{
	int u1=1,u2=n;
	int mid=0;
	while(u1<=u2)
	{
		mid=(u1+u2)/2;
	    if(s[mid].data<x)
	    {
	    	u1=mid+1;
		}
		else if(s[mid].data>x)
		{
			u2=mid-1;
		}
		else
		{
			return mid;
		}
	}
	return u2;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值