题解:P6032 选择客栈 加强版

· · 题解

题目传送门

Part 0:这篇题解是怎么来的

我们老师出了一场比赛,题目全是洛谷原题,这道我场切了(说实话我能自己做出来道绿真不容易),看了题解发现大家的思路和我都不一样,所以打算写这篇题解 (管理大大求过)

Part 1:思路

看数据范围,n \leq 2 \times 10^6,有点大,O(n) 我又不会做(盯着那个数列我也盯不出来)。再看 kpk \leq 10^4p \leq 10^2,好像可以从这俩入手。

处理完了之后就可以直接枚举色调,在同一个色调的客栈中枚举第一个人住的客栈(默认第二个人住的客栈在第一个人住的客栈的后面),然后往后找离第一个人住的客栈最近的咖啡店,直接二分就行,然后这个咖啡店往后的每一个与第一个人住的客栈色调相同的客栈都可以是一种选择,往后找到离那个咖啡店最近的与第一个人住的客栈同色调的客栈,看看位置,用结尾位置一减就行。 最后发现时间复杂度还是 $O(n \log n)$,主要是代码常数挺小的,所以并没有炸掉。 ## Part 2:细节 - 存相同色调的客栈时,用二维数组会炸空间,所以得用 `vector` 数组。 - 找咖啡厅和第二个客栈时都得用 `lower_bound`,因为住的客栈和选的咖啡厅可以在同一位置。 - 两个 `lower_bound` 可能会使选的两个客栈位置重叠,所以要减掉这一部分方案,发现当且仅当第一个客栈选在可以当作咖啡厅的地方的时候,才会发生重叠现象,所以我们可以在最开始统计最低消费不超过 $k$ 的咖啡店的时候就直接将答案减掉满足条件的咖啡厅的数量。 - Part 1 中,`第一个人住的客栈` 出现了五次,你注意到了吗?~~(你就说这是不是细节吧)~~ ## Part 3:代码 ``` #include<bits/stdc++.h> #define int long long #define PII pair< int, int > using namespace std; const int N = 1e4 + 5, mod = 998244353; int n, k, p, daan; vector< int > fg[N], jg; // fg 是风格的拼音(当时老师改原题把色调改成了风格),jg 是价格的拼音 template< typename T >inline void read(T &x){bool f=1;x=0;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=!f;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}x=(f?x:-x);return;} template< typename T, typename ... L > void read(T &a , L && ... b) { read(a); read(b ...); } int ksm(int a, int b, int p){int ans = 1;while(b){if(b & 1)ans =(ans*a)%p;b >>= 1;a = (a*a) % p;}return ans;} signed main(){ // freopen("a.in", "r", stdin); // freopen("a.out","w",stdout); read(n, k, p); for (int i = 1; i <= n; i++){ int a, b; read(a, b); fg[a+1].push_back(i); // 这里加一是为了从一开始枚举 if (b <= p){ jg.push_back(i); daan--; // 每有一个满足条件的咖啡厅,就会出现一次重叠现象 } } for (int i = 1; i <= k; i++){ for (int u : fg[i]){ auto a = lower_bound(jg.begin(), jg.end(), u); if (a == jg.end()) break; auto v = lower_bound(fg[i].begin(), fg[i].end(), *a); daan += fg[i].end() - v; // 本来应该是最后一个客栈的位置减 v 加一,由于 .end 指的是最后一个位置再往后一位,减一加一抵消了 } } printf("%lld", daan); return 0; } ```