这道题首先第一步把 T 求出来就好求了。
1. T
T 可以用二分(还是看代码吧):
int le=0,ri=l;
while(le<=ri)
{
int mid=(le+ri)/2;
if(check(mid))ri=mid-1;
else le=mid+1;
}
check:
bool check(int xt)
{
int tot=0;
int lt=1,rt=n;
for(int i=1;i<=n;i++)
{
int ct=c[i].d==1?l-c[i].x:c[i].x;
if(ct>xt)continue;
c[i].d==1?tot+=c[rt--].w:tot+=c[lt++].w;
}
return tot*2<half?0:1;
}
我还有个思路,就是按到牛棚距离排序模拟,不过没写代码,感觉复杂度应该是和上面一样(其实是排序 $n\log n$,自己觉得排序以外能 $O(n)$,有空写了 AC 的话我会更新)
2. 相遇次数
知道 T 了之后,奶牛的重量就没用了,这里,两个奶牛碰撞和互相穿过是等效的。
我们把向右移动的奶牛存在一个数组中并排序,向左移动的奶牛与他们相遇的次数就是奶牛左侧 $2T$ 以内的(包括边界)奶牛数。
二分 $\times$2:
if(c[i].d==-1)
{
le=1;
ri=cnt;
while(le<=ri)
{
int mid=(le+ri)/2;
if(c[i].x>=pos[mid])le=mid+1;
else ri=mid-1;
}
ans+=ri;
le=1;
while(le<=ri)
{
int mid=(le+ri)/2;
if(c[i].x-pos[mid]>t)le=mid+1;
else ri=mid-1;
}
ans-=ri;
}
Code
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
struct node
{
int w,x,d;
}c[50001];
int t,n,l,half=0,pos[50001];
bool cmp(node a,node b)
{
return a.x<b.x;
}
bool check(int xt)
{
int tot=0;
int lt=1,rt=n;
for(int i=1;i<=n;i++)
{
int ct=c[i].d==1?l-c[i].x:c[i].x;
if(ct>xt)continue;
c[i].d==1?tot+=c[rt--].w:tot+=c[lt++].w;
}
return tot*2<half?0:1;
}
int main()
{
cin>>n>>l;
for(int i=1;i<=n;i++)scanf("%d%d%d",&c[i].w,&c[i].x,&c[i].d),half+=c[i].w;
sort(c+1,c+1+n,cmp);
int le=0,ri=l;
while(le<=ri)
{
int mid=(le+ri)/2;
if(check(mid))ri=mid-1;
else le=mid+1;
}
t=le;
t*=2;
int cnt=0;
long long ans=0;
for(int i=1;i<=n;i++)if(c[i].d==1)pos[++cnt]=c[i].x;
// sort(pos+1,pos+cnt+1);
for(int i=1;i<=n;i++)
if(c[i].d==-1)
{
le=1;
ri=cnt;
while(le<=ri)
{
int mid=(le+ri)/2;
if(c[i].x>=pos[mid])le=mid+1;
else ri=mid-1;
}
ans+=ri;
le=1;
while(le<=ri)
{
int mid=(le+ri)/2;
if(c[i].x-pos[mid]>t)le=mid+1;
else ri=mid-1;
}
ans-=ri;
}
cout<<ans<<endl;
return 0;
}