CF1039E Summer Oenothera Exhibition 贪心、分治、倍增

传送门


感谢这一篇博客的指导(Orzwxh)

$PS$:默认数组下标为$1$到$N$

首先很明显的贪心:每一次都选择尽可能长的区间

不妨设$d_i$表示在取当前$K$的情况下,左端点为$i$的所有满足条件的区间中最大的右端点$+1$,然后连边$(i,d_i)$

那么我们就需要求一条链的长度,并支持动态修改某一些边

是不是有些印象?与弹飞绵羊极为相似,没有做过的可以先去感受一下……

上面那道题有两种做法:$LCT$与分块,所以这一道题就衍生出了$O(n\sqrt{n}logn)$的基于$LCT$的做法(安利chd的题解qaq)和$O(n^\frac{5}{3} + n^\frac{4}{3}logn)$的基于分块的做法。下面要谈的是后者。

首先因为输入中$K$的变化是无序的,所以修改会十分麻烦。不妨将询问离线,按照$W-K$从小到大排序,那么$d_i$的改变就是不降的。

设块长为$a$,又设$pre_i$,它需满足$pre_i - i \leq a$、$d_{pre_i} - i > a$、$pre_i$与$i$在同一条链上(如果对于链上的所有点都不满足条件,则令$pre_i=N+1$表示可以直接从$i$点经过不超过一个块长跳出去),维护$cnt_i$表示$i$与$pre_i$之间的点的个数(不计$pre_i$)。

对于每一次查询操作,我们就从$1$开始跳$pre$,每一次都加上对应的$cnt$,那么询问的答案就是$\sum cnt - 1$($1$不需要被算进去)。

但是会发现如果$d_i - i > a$,$pre_i=i$就会死循环,所以我们对与$pre_i=i$的情况使用倍增找到它下一次要跳到哪一个点。

可以知道查询的复杂度是$O(\frac{n}{a}qlogn + \frac{n^2}{a})$

对于修改,每一个点$i$最多都会被修改$a$次,每一次对于点$i$的修改我们只需要维护$i-a$到$i-1$的$pre$和$cnt$,修改的复杂度就是$O(n \times a^2)$

可知在$a = n^\frac{1}{3}$时该算法取到最小复杂度$O(n^\frac{5}{3} + n^\frac{5}{3}logn)$

喂这个算法和暴力有什么区别啊

可以发现我们的复杂度瓶颈在查询的倍增上,考虑如何优化倍增的复杂度。

因为当$d_i - i > n^\frac{1}{3}$时,前面的所有点的$pre$都是不会因为$d_i$的改变而改变的,所以当$d_i - i > n^\frac{1}{3}$时,单次修改的复杂度变为了O(1)

所以我们可以考虑:当$n^\frac{1}{3} < d_i - i \leq b$时暴力移动$d_i$的值进行更新,当$d_i - i > b$时进行倍增

那么查询的复杂度就变为$O(n^\frac{5}{3} + \frac{n}{b}qlogn)$,修改的复杂度变为了$O(n^\frac{5}{3} + nb)$当$b=n^\frac{2}{3}$时总复杂度取到最小值$O(n^\frac{5}{3} + n^\frac{4}{3}logn)$

然后这道题就做完了

所以这道题的思路就是一个奇怪的与根号分治相似的东西,有点三合一的意思

①$d_i-i \leq n^\frac{1}{3}$,通过维护$pre_i$与$cnt_i$压缩路径

②$n^\frac{1}{3} < d_i-i \leq n^\frac{2}{3}$,暴力移动$d_i$位置进行更新

③$n^\frac{2}{3} < d_i-i$,倍增找到下一次更新的点

一些实现上的细节:

①在$d_i - i \leq n ^ \frac{1}{3}$部分判断哪一些点需要修改可以使用堆进行维护(再在这里Orzwxh),维护$[i,d_i]$的极差然后扔到堆里面,每一次小于等于当前$W-K$的堆中的点就是需要重新更新$pre$与$cnt$的点

②倍增使用$ST$表进行实现

③维护$n^\frac{1}{3} < d_i - i \leq n^\frac{2}{3}$的部分在计算答案时进行更新即可,不需要额外维护

④根据③,在$d_i - i \leq n ^ \frac{1}{3}$时,不能直接把$pre_i$的贡献算上然后跳到$nxt_{pre_i}$,因为$nxt_{pre_i}$可能还没有更新导致跳到一些奇怪的地方

  1 #include<bits/stdc++.h>
  2 #define PII pair < int , int >
  3 #define st first
  4 #define nd second
  5 //This code is written by Itst
  6 using namespace std;
  7 
  8 inline int read(){
  9     int a = 0;
 10     char c = getchar();
 11     bool f = 0;
 12     while(!isdigit(c))
 13         c = getchar();
 14     while(isdigit(c)){
 15         a = (a << 3) + (a << 1) + (c ^ '0');
 16         c = getchar();
 17     }
 18     return f ? -a : a;
 19 }
 20 
 21 inline int max(int a , int b){
 22     return a > b ? a : b;
 23 }
 24 
 25 inline int min(int a , int b){
 26     return a < b ? a : b;
 27 }
 28 
 29 const int MAXN = 1e5 + 10;
 30 int N , W , Q , logN , j_small , j_big;
 31 int num[MAXN] , logg2[MAXN] , ST[21][MAXN][2] , pre[MAXN] , nxt[MAXN] , nMax[MAXN] , nMin[MAXN] , cnt[MAXN] , len[MAXN] , ans[MAXN];
 32 bool vis[MAXN];
 33 priority_queue < PII > q , mod;
 34 deque < int > v;
 35 
 36 void input(){
 37     N = read();
 38     j_small = pow(N , 1.0 / 3);
 39     j_big = pow(N , 2.0 / 3);
 40     W = read();
 41     Q = read();
 42     for(int i = 1 ; i <= N ; ++i)
 43         num[i] = ST[0][i][0] = ST[0][i][1] = read();
 44     for(int i = 1 ; i <= Q ; ++i)
 45         q.push(PII(read() , i));
 46 }
 47 
 48 void init(){
 49     for(int i = 1 ; 1 << i <= N ; ++i)
 50         for(int j = 1 ; j + (1 << i) - 1 <= N ; ++j){
 51             ST[i][j][0] = min(ST[i - 1][j][0] , ST[i - 1][j + (1 << (i - 1))][0]);
 52             ST[i][j][1] = max(ST[i - 1][j][1] , ST[i - 1][j + (1 << (i - 1))][1]);
 53         }
 54     logg2[0] = -1;
 55     for(int i = 1 ; i <= N ; ++i){
 56         logg2[i] = logg2[i >> 1] + 1;
 57         nxt[i] = i + 1;
 58         pre[i] = min(N + 1 , j_small + i);
 59         cnt[i] = pre[i] - i;
 60         nMax[i] = max(num[i] , num[i + 1]);
 61         nMin[i] = min(num[i] , num[i + 1]);
 62         if(i != N)
 63             mod.push(PII(nMin[i] - nMax[i] , i));
 64     }
 65     logN = 1;
 66     while(logN << 1 <= N)
 67         logN <<= 1;
 68     logN = logg2[logN];
 69 }
 70 
 71 void upd(int p , PII t){
 72     mod.pop();
 73     while(nxt[p] <= N && nMax[p] - nMin[p] <= t.st && nxt[p] - p <= j_big){
 74         ++nxt[p];
 75         if(nxt[p] <= N){
 76             if(nMax[p] < num[nxt[p]])
 77                 nMax[p] = num[nxt[p]];
 78             if(nMin[p] > num[nxt[p]])
 79                 nMin[p] = num[nxt[p]];
 80         }
 81     }
 82     if(nxt[p] <= N && nxt[p] - p <= j_small)
 83         mod.push(PII(nMin[p] - nMax[p] , p));
 84     pre[p] = p;
 85     cnt[p] = len[p] = 0;
 86     v.clear();
 87     v.push_back(p);
 88     vis[p] = 1;
 89     while(pre[p] <= N && nxt[pre[p]] - p <= j_small){
 90         pre[p] = nxt[pre[p]];
 91         ++cnt[p];
 92         v.push_back(pre[p]);
 93     }
 94     for(int i = p - 1 ; i && i >= p - j_small ; --i)
 95         if(vis[nxt[i]]){
 96             while(v.back() - i > j_small)
 97                 v.pop_back();
 98             pre[i] = v.back();
 99             cnt[i] = len[nxt[i]] + v.size();
100             len[i] = len[nxt[i]] + 1;
101             vis[i] = 1;
102         }
103     memset(vis + max(p - j_small , 1) , 0 , sizeof(bool) * (j_small + 1));
104 }
105 
106 void work(){
107     while(!q.empty()){
108         PII t = q.top();
109         q.pop();
110         t.st = W - t.st;
111         while(!mod.empty() && -mod.top().st <= t.st)
112             upd(mod.top().nd , t);
113         int p = 1 , all = 0;
114         while(p <= N){
115             int cur = nxt[p] - p;
116             if(cur <= j_small){
117                 all += cnt[p];
118                 p = pre[p];
119             }
120             else{
121                 ++all;
122                 while(nxt[p] <= N && nMax[p] - nMin[p] <= t.st && cur <= j_big){
123                     ++nxt[p];
124                     ++cur;
125                     if(nxt[p] <= N){
126                         if(nMax[p] < num[nxt[p]])
127                             nMax[p] = num[nxt[p]];
128                         if(nMin[p] > num[nxt[p]])
129                             nMin[p] = num[nxt[p]];
130                     }
131                 }
132                 if(cur <= j_big)
133                     p = nxt[p];
134                 else{
135                     int cMax = 0 , cMin = 1e9;
136                     for(int j = logN ; j >= 0 ; --j)
137                         if(p + (1 << j) - 1 <= N){
138                             int m = max(cMax , ST[j][p][1]) , n = min(cMin , ST[j][p][0]);
139                             if(m - n <= t.st){
140                                 cMax = m;
141                                 cMin = n;
142                                 p += 1 << j;
143                             }
144                         }
145                 }
146             }
147         }
148         ans[t.nd] = all;
149     }
150 }
151 
152 void output(){
153     for(int i = 1 ; i <= Q ; ++i)
154         printf("%d\n" , ans[i] - 1);
155 }
156 
157 int main(){
158 #ifndef ONLINE_JUDGE
159     freopen("in" , "r" , stdin);
160     freopen("out" , "w" , stdout);
161 #endif
162     input();
163     init();
164     work();
165     output();
166     return 0;
167 }

猜你喜欢

转载自www.cnblogs.com/Itst/p/10205167.html