【洛谷】P5677 [GZOI2017]配对统计(离线+树状数组)
题意
给定 个正整数,对于一组匹配 ,如果对于任意的 ,满足 。那么就称 为一组好的匹配。
给出 询问,每次询问区间 中好的匹配的数量。
记第 次询问的答案为 ,那么最终只需输出 。
思路
首先观察题目中对于“好的匹配”的定义,可以发现。对于一个数 ,能和它组成好的匹配的数 一定满足 的值最小,也就是 是与 相差最小的数。于是就可以想到排序,将原数组排序后,对于任意的 。能和 组成好的匹配的数一定在 和 中( 和 需要特判) 。于是就可以在 的时间内求出所有的好的匹配。
然而对于题目中的查询操作。有一个很 navie 的想法:用树状数组 维护区间 内的配对数量,对于每一个 (设 的),直接 。在查询的时候输出 。
可惜这样的想法是错误的,因为在相减的时候只减去了 时的不合法配对,并没有减去 时的不合法配对。
注意到本题中并未要求支持修改操作,于是可以考虑离线做法。
上述错误做法出现的问题是并未减去 时的不合法配对。如果将 改成 ,可以发现当 时, 得到的答案就是正确的了,因为此时的 中只限制了 ,所以 的不合法配对也被计算在其中。也就可以在被减去了。
但是一旦当 时,这样的做法又是错误的,因为此时 中的配对的 可以比 还大。相减时就无法减去这些不合法匹配。
那如果在树状数组中不记录这些不合法匹配呢?
于是可以想到先将配对按照 的大小进行排序。再将所有的询问离线,先记录下来,再按照右端点 的大小进行排序。在求解每一次询问时,都只把 的配对记录到树状数组中。这样就避免了 的不合法匹配。
关于记录到树状数组中的信息,因为我们想要的是所有满足 的匹配数量,所以可以直接在树状数组中记录 ,这样就可以 得到所有匹配的数量。
最后,别忘了开 long long。
code:
#include<cstdio>
#include<algorithm>
using namespace std;
#define LL long long
const int N=3e5+10;
const int M=3e5+10;
int n,m,c[N];
LL ans;
struct node{
int val,id;
bool operator <(const node &t)const{
return val<t.val;
}
}a[N];
struct match{
int l,r;
bool operator <(const match &t)const{
if(r!=t.r)return r<t.r;
return l<t.l;
}
}p[N<<1];//除了最大和最小的数,极端情况下每个数都能有两个好的匹配,所以数组大小要*2
int tot=0;
struct quest{
int l,r,id;
bool operator <(const quest &t)const{
if(r!=t.r)return r<t.r;
return l<t.l;
}
}q[M];
int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}
int lowbit(int x){return x&(-x);}
void updata(int x,int k)
{
for(int i=x;i<=n;i+=lowbit(i)) c[i]+=k;
}
int query(int x)
{
int res=0;
for(int i=x;i;i-=lowbit(i)) res+=c[i];
return res;
}
void add(int a,int b)
{
p[++tot].l=min(a,b);
p[tot].r=max(a,b);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i].val),a[i].id=i;
sort(a+1,a+n+1);
for(int i=2;i<n;i++)
{
int res1=a[i].val-a[i-1].val,res2=a[i+1].val-a[i].val;
if(res1<res2) add(a[i].id,a[i-1].id);
else if(res1>res2) add(a[i].id,a[i+1].id);
else add(a[i].id,a[i-1].id),add(a[i].id,a[i+1].id);//当两边都相等时当然就都是好的匹配
}
add(a[1].id,a[2].id);
add(a[n-1].id,a[n].id);//特判
sort(p+1,p+tot+1);
for(int i=1;i<=m;i++) scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;//别忘了记录是第i个询问的答案还要*i
sort(q+1,q+m+1);
int now=1;
for(int i=1;i<=m;i++)
{
while(now<=tot&&p[now].r<=q[i].r) updata(n-p[now++].l,1);
ans+=(LL)q[i].id*query(n-q[i].l);
}
printf("%lld\n",ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律