正题
题目链接:https://www.luogu.com.cn/problem/AT4353
题目大意
数轴上有nnn个球mmm个洞,每次可以将所有球左移或者右移,球到洞的位置会掉下去。
求有多少让球掉进不同洞的方案。
1≤n,m≤1051\leq n,m\leq 10^51≤n,m≤105
解题思路
设一个球距离左右洞的距离分别为(x,y)(x,y)(x,y),那么一个球(x1,y1)(x_1,y_1)(x1,y1)对球(x2,y2)(x_2,y_2)(x2,y2)产生限制当且仅当x1≤x2x_1\leq x_2x1≤x2且y1≥y2y_1\geq y_2y1≥y2。
那么我们相当于找一些两两没有限制的数字的集合,一种方法是按照xxx升序排序,然后求yyy的上升序列数量即可。
时间复杂度O(nlogn)O(n\log n)O(nlogn)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define lowbit(x) (x&-x)
#define mp(x,y) make_pair(x,y)
using namespace std;
const ll N=1e5+10,P=1e9+7;
ll n,m,cnt,bnt,b[N],x[N],y[N],t[N],ans;
pair<ll ,ll >a[N];
void Change(ll x,ll val){while(x<=n){(t[x]+=val)%=P;x+=lowbit(x);}return;
}
ll Ask(ll x){ll ans=0;while(x){(ans+=t[x])%=P;x-=lowbit(x);}return ans;
}
signed main()
{freopen("hole.in","r",stdin);freopen("hole.out","w",stdout);scanf("%lld%lld",&n,&m);for(ll i=1;i<=n;i++)scanf("%lld",&x[i]);for(ll i=1;i<=m;i++)scanf("%lld",&y[i]);ll z=1;for(ll i=1;i<=n;i++){while(z<=m&&y[z]<x[i])z++;if(z==1||z>m||y[z]==x[i])continue;a[++cnt]=mp(x[i]-y[z-1],x[i]-y[z]);b[++bnt]=y[z]-x[i];}sort(a+1,a+1+cnt);b[++bnt]=0;sort(b+1,b+1+bnt);cnt=unique(a+1,a+1+cnt)-a-1;bnt=unique(b+1,b+1+bnt)-b-1;Change(1,1);ans=1;for(ll i=1;i<=cnt;i++){ll w=lower_bound(b+1,b+1+bnt,-a[i].second)-b;;ll sum=Ask(w-1);Change(w,sum);(ans+=sum)%=P;}printf("%lld\n",ans);return 0;
}