题目大意
有n个数,m个查询,对于每个查询,询问指定区间,有多少个数对的绝对值小于等于2。
解题思路
莫队O^1.5
首先将询问离线处理左端点进行编号,每sqrt(n)个为一组
sort结构体 当左端点编号相同时,比较右端点大小。小的放在前面。
对于每组询问暴力处理,只需处理当前新加入(删除的数字在当前区间内有多少点和它的绝对值只差小于2即可)
唯一要注意的是加点是先更新答案再计数,删点是先计数器-1再更新答案
因为对于每个询问,左端点的修改量不超过sqrt(n)
右端点每一组最坏的复杂度是修改n次 共sqrt(n)组
m,n相当的情况下,莫队算法整体的时间复杂度是n^1.5
code:
#include <cstdio> #include <iostream> #include <algorithm> #include <ctime> #include <cctype> #include <cmath> #include <string> #include <cstring> #include <stack> #include <queue> #include <list> #include <vector> #include <map> #include <set> #define sqr(x) ((x)*(x)) #define LL long long #define INF 0x3f3f3f3f #define PI acos(-1.0) #define eps 1e-10 using namespace std; int n,m,bl; int cnt[100010]; int a[100010]; struct node{ int l,r,x; }s[100005]; LL anss[100005]; int cmp(node a,node b) { if (a.l/bl==b.l/bl) return a.r<b.r; return (a.l/bl)<(b.l/bl); } inline LL count(int x) { return cnt[x-2]+cnt[x-1]+cnt[x]+cnt[x+1]+cnt[x+2]; } int main(int argc, char const *argv[]) { int ca=0; while (~scanf("%d%d",&n,&m)) { bl=sqrt((double)n); for (int i=1;i<=n;i++){ scanf("%d",&a[i]); a[i]+=5;//为了防止处理边界带来的多余的复杂度,直接全部加上5 } for (int i=1;i<=m;i++) { scanf("%d%d",&s[i].l,&s[i].r); s[i].x=i; } sort(s+1,s+1+m,cmp); memset(cnt,0,sizeof cnt); int l=1,r=1; cnt[a[1]]=1; LL ans=0; for (int i=1;i<=m;i++) { if (r<s[i].r){ for (;r-s[i].r;) { r++; ans+=count(a[r]); cnt[a[r]]++; } } if (r>s[i].r){ for (;r-s[i].r;) { cnt[a[r]]--; ans-=count(a[r]); r--; } } if (l<s[i].l){ for (;l-s[i].l;) { cnt[a[l]]--; ans-=count(a[l]); l++; } } if (l>s[i].l){ for (;l-s[i].l;) { l--; ans+=count(a[l]); cnt[a[l]]++; } } anss[s[i].x]=ans; } printf("Case %d:\n", ++ca); for (int i = 1; i <= m; ++i) { printf("%lld\n",anss[i]); } } return 0; }
时间: 2024-10-22 03:09:44