Chika and Friendly Pairs
网课是真的烦人,我不想学习,我只想打代码啊。
————————————————————————————
这题是2019湖南ccpc邀请赛的C题,比赛时的定位是一道银牌数据结构题。这场比赛是我人生第一场现场赛,现在回忆起来当时真的是惨不忍睹。。。。。。最搞笑的是我当时竟然对着这道题想了好久,还是在完全不知道做法的情况下在那里乱搞,真的是有够天真。
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6534
先说做法 离散化(二分)+ 树状数组+ 莫队
题目中让我们求的友好数对其实就是对于一个ai,求区间内满足条件 -k <= ai - aj <= k 的aj的个数,这可以用树状数组来维护(有点像用树状数组维护逆序对),然后查询的时候再用莫队来查。
其实想明白的话这题不算太难,但是写起来还是有很多要注意的东西的,这题的时限卡的很紧,稍微写的不好就会T(像我这种一言不合就喜欢用stl的人形大常数真的被T的很难受)
我原本的想法是把所有区间内的数字都进来的同时,顺便把a[i] + k 和 a[i] - k 也一起读进来,然后一道离散化了到时候树状数组一起查,但是仔细一想觉得这样的话莫队的add和remove不是很好写。
就换了种写法(我认为相对简单一些),只是把区间的数字都进来,离散化后在每次查询时只需查一下数字的上下界a[i] + k 和 a[i] - k ,上下界用二分查。其实和一种写法原理一样。但是这种写法需要注意得是如果每次查询的时候都用二分来查询的话算法复杂度就增加一个logn,就会超时,所以我们就先预处理就好了。
代码附上
#include <stdio.h> #include <iostream> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <map> #include <stack> #pragma GCC optimize(2) #define mm(i,v) memset(i,v,sizeof i); #define mp(a, b) make_pair(a, b) #define one first #define two second //你冷静一点,确认思路再敲!!! using namespace std; typedef long long ll; typedef pair<int, int > PII; const int N = 3e5 + 5, mod = 1e9 + 9, INF = 0x3f3f3f3f; int n, m, k, block, res; int a[N], tr[N], c[N], low[N], up[N], answer[N]; vector<int> vec; //map<int, int> q; inline int read(){ char c=getchar();int x=0,f=1; while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();} return x*f; } struct node { int l, r, id; }list[N]; bool cmp(node a, node b) { if (a.l / block != b.l / block) return a.l < b.l; else return a.r < b.r; } int lowbit(int x) { return x & -x; } void add(int x, int y) { for (int i = x; i <= 28000; i += lowbit(i)) c[i] += y; } int sum(int x) { int ans = 0; for (int i = x; i > 0; i -= lowbit(i)) { ans += c[i]; } return ans; } int find(int x) { // 二分来查找离散化后的数字 int pos = upper_bound(vec.begin(), vec.end(), x) - vec.begin(); return pos; } int main() { n = read(); m = read(); k = read(); for (int i = 1; i <= n; ++i) { a[i] = read(); vec.push_back(a[i]); // vec.push_back(a[i] - k); // vec.push_back(a[i] + k); } block = sqrt(m); for (int i = 1; i <= m; ++i) { list[i].l = read(); list[i].r = read(); list[i].id = i; } sort(list + 1, list + 1 + m, cmp); sort(vec.begin(), vec.end()); vec.erase(unique(vec.begin(), vec.end()), vec.end()); // 预处理每个数的上下界,不然可能会T for (int i = 1; i <= n; ++i) { low[i] = lower_bound(vec.begin(), vec.end(), a[i] - k) - vec.begin(); up[i] = upper_bound(vec.begin(), vec.end(), a[i] + k) - vec.begin(); } // for (int i = 1; i <= n; ++i) // cout << find(a[i]) << endl; res = 0; int curL = 1, curR = 0; for (int i = 1; i <= m; ++i) { int l = list[i].l, r = list[i].r; while (curL < l) { add(find(a[curL]), -1); res -= sum(up[curL]) - sum(low[curL]); curL++; } while (curL > l) { curL--; res += sum(up[curL]) - sum(low[curL]); add(find(a[curL]), 1); } while (curR < r){ curR++; res += sum(up[curR]) - sum(low[curR]); add(find(a[curR]), 1); } while (curR > r) { add(find(a[curR]), -1); res -= sum(up[curR]) - sum(low[curR]); curR--; } answer[list[i].id] = res; } for (int i = 1; i <= m; ++i) printf("%d\n", answer[i]); }
对了,这题杭电的数据可能有点弱? 我看到一个代码没有离散化直接ac??? 然后我自己随便手撸了个样例都过不了。。。。。或许这就是莫队的玄学?(溜)