20.2.19排位赛D

【题目大意】
位置0与位置L是谷仓。在数轴(0,L)的整点上有n只牛(1<=n<=10^5),每只牛有重量,位置(1<=L<= 10 ^9,每只牛的位置均不相同),以及速度(1单位距离/单位时间或-1单位距离/单位时间,1表示向右走,-1表示向左走),当牛走到谷仓(0或L)时停止移动。若两只牛在(0,L)上(可以不在整数点)相遇,它们会交换速度(即反弹,原本速度为1的牛速度变成-1,速度为-1的牛速度变成1)。问当仓库里的牛的重量大于等于所有牛的重量的二分之一时,求牛相遇的次数。
【解题思路】
先将n只牛按位置从小到大排好。
进去谷仓的牛的顺序一定是从两端再到中间(想象一下n个人站在独木桥上,n个人到达岸上的顺序)
根据题意,两只牛相遇时会反弹,由于两只牛的速率是相等的,可以把这个过程看成"穿透"(即两只牛互相穿过,交换了重量)。
假设现在有一只牛a,牛a向右走,牛a前面没有向右走的牛。
牛a的重量归到0谷仓还是L谷仓与牛a位置后面是否有向左走的牛有关。
如果牛a后面有向左走的牛(牛b),那么牛a的重量归到0谷仓。
而且牛a的重量归到0谷仓的时间的数值就是牛b位置的数值。

现在有一只牛a,牛a向左走,牛a后面没有向左走的牛。
牛a的重量归到0谷仓还是L谷仓与牛a位置前面是否有向右走的牛有关。
如果牛a前面有向右走的牛(牛b),那么牛a的重量归到L谷仓。
而且牛a的重量归到L谷仓的时间的数值就是L减去牛b位置的数值。

因此,n只牛中有x只牛向左走,有y只牛向右走。
那么前x个重量会归为0谷仓,后y个重量会归为L谷仓。
对于前x个重量,第i个重量归为0谷仓的时间的数值等于第i个向左走的牛的位置。
对于后y个重量,(从左往右)第i个重量归为L谷仓的时间的数值等于L减去第i个向右走的牛的位置。

根据以上信息可以求出仓库里的牛的重量大于等于所有牛的重量的二分之一的时间t。算出t秒后n只牛的位置,排序,再求逆序对,逆序对就是牛相遇的次数。
【代码】

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
struct data
{
	int w,d;
	long long x;
}a[101010];
struct node
{
	long long v;
	int num;
}b[101010];
int c[101010];
queue <int> QL,QR;
int n;
long long Len,sum,ans=0,st;;
bool cmp1(data x,data y)
{
	return (x.x<y.x);
}
bool cmp2(node x,node y)
{
	return (x.v<y.v || (x.v==y.v && x.num>y.num));
}
int lowbit(int x)
{
	return (x&(-x));
}
void update(int x,int k)
{
	while (x<=n)
	{
		c[x]+=k;
		x+=lowbit(x);
	}
}
long long query(int x)
{
	long long ans=0;
	while (x>0)
	{
		ans+=c[x];
		x-=lowbit(x);
	}
	return ans;
}
void Get_st()
{
	int l=1,r=n;
	long long t=0;
	QL.push(n+1);
	QR.push(0);
	a[0].x=-9999999999;
	a[n+1].x=9999999999;
	while (true)
	{
		int RR=QR.front();
		int LL=QL.front();
		if (a[RR].x==-9999999999 && a[LL].x==9999999999) break;
		if (a[LL].x<Len-a[RR].x)
		{
			t+=a[l].w;
			QL.pop();
			l++;
			st=a[LL].x;
		}else if (a[LL].x>Len-a[RR].x)
		{
			t+=a[r].w;
			QR.pop();
			r--;
			st=Len-a[RR].x;
		}else
		{
			t+=a[l].w;
			QL.pop();
            t+=a[r].w;
			QR.pop();
			l++;
			r--;
			st=a[LL].x;
		}
		if (t*2>=sum) break;
	}
	//cout<<st<<endl;
}
int main()
{
	//freopen("d.in","r",stdin);
	//freopen("d.out","w",stdout);
	cin>>n>>Len;
	for (int i=1;i<=n;i++)
	{
	  cin>>a[i].w>>a[i].x>>a[i].d;
	  sum=sum+a[i].w; 
    }
	sort(a+1,a+n+1,cmp1);  
    for (int i=1;i<=n;i++)
      if (a[i].d==-1) QL.push(i);
    for (int i=n;i>=1;i--)
	  if (a[i].d== 1) QR.push(i);
	Get_st(); 
	for (int i=1;i<=n;i++)
	  {
	  	b[i].v=a[i].x+a[i].d*st;
	  	b[i].num=i;
	  }
	sort(b+1,b+n+1,cmp2);  
	for (int i=1;i<=n;i++)
	  {
	  	 long long nbr=query(n)-query(b[i].num);
	  	 update(b[i].num,1);
	  	 ans+=nbr;
	  }
	cout<<ans<<endl;   
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值