P1065 · OFFICIAL SOLUTION

P1065 咖啡温律录 官方题解

Gioush OJ · P1065 咖啡温律录

【部分分:测试点 1∼41\sim 41∼4】

  • 这一档中温度上界只有 10310^3。一个显然的思路是枚举每一个整数温度 xx,再扫描全部配方,统计满足 lixril_i\leq x\leq r_i 的配方数量。数量不少于 kk 时,就将 xx 标记为已经写入温律录。
  • 对一次询问,直接枚举 [aj,bj][a_j,b_j] 中的全部温度并统计标记数量。设温度上界为 VV,单组数据的时间复杂度为 O(V(n+q))\mathcal{O}(V(n+q)),空间复杂度为 O(V)\mathcal{O}(V)

【部分分:测试点 5∼85\sim 85∼8】

  • 上一档反复判断一个温度是否落在配方区间中。我们可以考虑使用差分数组,令 dlidli+1d_{l_i}\gets d_{l_i}+1dri+1dri+11d_{r_i+1}\gets d_{r_i+1}-1。求前缀和后,sx=i=1xdis_x=\sum_{i=1}^{x}d_i 正好表示认可温度 xx 的配方数量。
  • 再令 bx=[sxk]b_x=[s_x\geq k],并预处理 px=i=1xbip_x=\sum_{i=1}^{x}b_i。询问 [aj,bj][a_j,b_j] 的答案就是 pbjpaj1p_{b_j}-p_{a_j-1}。时间复杂度为 O(n+q+V)\mathcal{O}(n+q+V),空间复杂度为 O(V)\mathcal{O}(V)

【部分分:测试点 9∼129\sim 129∼12】

  • 这一档满足 k=1k=1,所以温律录恰好是全部配方区间的并。根据这一性质,我们将区间按照左端点排序,依次合并有交集或相邻的区间,可以得到若干两两分离的闭区间。
  • 预处理每个合并区间包含的整数数量及其前缀和。一次询问只需二分找到与 [aj,bj][a_j,b_j] 相交的第一个和最后一个合并区间,再处理两侧不完整的部分。时间复杂度为 O((n+q)logn)\mathcal{O}((n+q)\log n),空间复杂度为 O(n)\mathcal{O}(n)

【正解】

  • 完整数据的温度上界仍然只有 2×1052\times 10^5,因此直接使用第二档的差分与两次前缀和即可。第一次前缀和求每个温度被多少张配方认可,第二次前缀和求温律录在任意前缀中包含多少个温度。
  • 每个配方只产生两个差分修改,每个询问只进行一次区间作差。单组数据的时间复杂度为 O(n+q+V)\mathcal{O}(n+q+V),空间复杂度为 O(V)\mathcal{O}(V)。多组数据之间要清空本组实际使用的数组范围。

【参考代码】

#include <cstdio>#include <cstring> #define MAXV 200010 using namespace std; int c,T,n,k,q,Diff[MAXV],Pre[MAXV]; void Solve() {    memset(Diff,0,sizeof(Diff));    memset(Pre,0,sizeof(Pre));    scanf("%d%d%d",&n,&k,&q);    for (int i=1,l,r;i<=n;i++) {        scanf("%d%d",&l,&r);        Diff[l]++;        Diff[r+1]--;    }    int Now=0;    for (int i=1;i<MAXV;i++) {        Now+=Diff[i];        Pre[i]=Pre[i-1]+(Now>=k);    }    for (int i=1,l,r;i<=q;i++) {        scanf("%d%d",&l,&r);        printf("%d\n",Pre[r]-Pre[l-1]);    }} int main() {    scanf("%d%d",&c,&T);    while (T-->0) Solve();    return 0;}