题解:P1296 奶牛的耳语
__CrossBow_EXE__ · · 题解
前言
这题自从被我 hack 之后已经过去 3 天了,居然还能交题解,再补一个分块做法。
题解
序列是无序的,先排个序。排完序后,把序列分成
如果我们使用暴力做法,我们需要统计每头奶牛后面有多少奶牛与这头奶牛距离小于等于
下面是这种思路的代码:
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;
int n,d;
const int N=1000005;
int a[N],len;
int id[N],maxn[N];
int l[N],r[N];//每块的左右端点
int lst;//最后一块的编号
ll ans=0;
signed main(int argc,char *argv[]){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
ios::sync_with_stdio(NULL);
cin.tie(0),cout.tie(0);
cin>>n>>d;
len=sqrt(n);//块长
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+n+1);//排序
for(int i=1;i<=n;i++){
id[i]=i/len+1;//块编号
lst=id[i];
if(id[i]!=id[i-1]){//新的一块
l[id[i]]=i;//这一块的左端点为当前点
r[id[i]-1]=i-1;//上一块的右端点为上一个点
maxn[id[i]-1]=a[i-1];//上一块的最大值为上一个数字
}
}
r[lst]=n;
maxn[lst]=a[n];
for(int i=1,t;i<=n;i++){
bool flag=0;
for(int j=i+1;j<=r[id[i]];j++){//遍历这一块其他元素
if(a[i]+d>=a[j]) ans++;
else{
flag=1;//找到比它大的了
break;
}
}
if(flag) continue;
for(int j=id[i]+1;j<=lst;j++){//遍历后面所有块
if(maxn[j]<=a[i]+d) ans+=r[j]-l[j]+1;//加上这一块的大小
else{
//这一块有比它大的
for(int k=l[j];k<=r[j];k++){
if(a[k]<=a[i]+d) ans++;
else break;
}
break;
}
}
}
cout<<ans<<endl;
return 0;
}
/*
---INFORMATIONS---
TIME:2025/5/16 9:22:10
PROBLEM:P1296
CODE BY __CrossBow_EXE__ Luogu uid967841
*/
还能小优化一下:把
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;
int n,d;
const int N=1000005;
int a[N],len;
int id[N],maxn[N];
int l[N],r[N];//每块的左右端点
int lst;//最后一块的编号
ll ans=0;
bool check(int i,int x){
if(maxn[x]<=a[i]+d) return 1;
else return 0;
}
signed main(int argc,char *argv[]){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
ios::sync_with_stdio(NULL);
cin.tie(0),cout.tie(0);
cin>>n>>d;
len=sqrt(n);//块长
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+n+1);//排序
for(int i=1;i<=n;i++){
id[i]=i/len+1;//块编号
lst=id[i];
if(id[i]!=id[i-1]){//新的一块
l[id[i]]=i;//这一块的左端点为当前点
r[id[i]-1]=i-1;//上一块的右端点为上一个点
maxn[id[i]-1]=a[i-1];//上一块的最大值为上一个数字
}
}
maxn[lst+1]=2147483647;
l[lst+1]=n+1;
r[lst]=n;
maxn[lst]=a[n];
for(int i=1;i<=n;i++){
bool flag=0;
for(int j=i+1;j<=r[id[i]];j++){//遍历这一块其他元素
if(a[i]+d>=a[j]) ans++;
else{
flag=1;//找到比它大的了
break;
}
}
if(flag) continue;
//二分,启动!!!
int lt=id[i]+1,rt=lst;
while(lt<=rt){
int mid=(lt+rt)>>1;
if(check(i,mid)) lt=mid+1;
else rt=mid-1;
}
ans+=l[lt]-r[id[i]]-1;
for(int j=l[lt];j<=r[lt];j++){
if(a[j]<=a[i]+d) ans++;
else break;
}
}
cout<<ans<<endl;
return 0;
}
/*
---INFORMATIONS---
TIME:2025/5/16 9:22:10
PROBLEM:P1296
CODE BY __CrossBow_EXE__ Luogu uid967841
*/
后记
本蒟蒻的第
自己 hack 的题写题解就是爽!