7 个月后再来看这道题,还是感觉太妙了。
由于答案最终输出 /(E /times Len/),所以本质上是问 /(/forall d /in[L, R]/) 的贡献和,再进一步想,亵渎的要求就是寻找序列
/[x_i=/varepsilon(/exists h_i| h_i/in [(i-1)d+1, id])
/]
从 /(i=1/) 开始的最长连续的 1 段,最长段不好求,转化为求结束的位置,设 /(pos_x/) 为高度 /(x/) 最早出现的位置(不存在则为 /(0/)),则结束的位置 /(end/) 应该满足:
/[/varepsilon(/nexists h_i| h_i/in [(end-1)d+1, end/times d])=/sum_{i=(end-1)d+1}^{end/times d} pos_i=0
/]
现在要搞定的就是这个东西了。
考虑没有增加随从操作怎么做,由于每一个不同的 /(d/) 最多形成的块有 /(n+n/2+n/3+/dots+n/n=n/ln(n)/) 个,我们直接暴力把这 /(n/ln(n)/) 个块的答案整出来(其实也就是对于每个 /(d/),把对应的 /(x_i/) 搞出)。
考虑记 /(time_{d, i}/) 为块长为 /(d/) 时,第 /(i/) 个块最早什么时候有 1,不难写出:
/[time_{d_,i}=/min_{(i-1)d+1/le j/le id} pos_j
/]
这个显然就可以 ST 表做出来了把,复杂度 /(O(n /log n+n/ln(n))/) 分别是 ST 表预处理的 /(O(n /log n)/),询问个数 /(n/ln(n)/) 乘上 ST 表询问复杂度 /(O(1)/) 的 /(O(n/ln(n))/)。
显然第 /(i/) 块产生贡献需要满足前 /(i-1/) 块都有值,所以我们还需要对每一个 /(time_{d, i}/) 取前缀 /(/max/)。
现在我们得到了这个 /(time_{d, i}/),也就相当于知道了这一块的贡献将在什么时候产生,直接丢到这个时刻上面就行了,单点修改、区间查询,显然树状数组更好。
复杂度 /(O(n/ln(n)/log n)=O(n/log^2n)/)。
Code:
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <vector>
#include <cctype>
using namespace std;
inline int read(){
char ch=getchar();int x=0, f=1;
while(!isdigit(ch)) f=(ch=='-'?-1:f), ch=getchar();
while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0', ch=getchar();
return x*f;
}
const int N=1e5+5;
const int M=1e6+5;
int n, m, lg[N], f[N][20], pos[N];
vector <int> V[M];
pair <int, int> box[M];
int query(int l, int r){//查询最小值
int k=lg[r-l+1];
return min(f[l][k], f[r-(1<<k)+1][k]);
}
int BIT[N];
#define lowbit(x) (x&(-x))
void add(int x){for(; x<=n; x+=lowbit(x)) BIT[x]++;}
int query(int x){
int ret=0;
for(; x; x-=lowbit(x)) ret+=BIT[x];
return ret;
}
#undef lowbit
inline int min(int a, int b){return a=(a<b?a:b);}
int main(){
n=read(), m=read();
for(int i=1; i<=n; i++) pos[i]=m+1;
for(int i=1, opt, x; i<=m; i++){
opt=read();
if(opt&1) x=read(), pos[x]=min(pos[x], i), box[i].first=false;
else box[i].first=read(), box[i].second=read();
}
for(int i=2; i<=n; i++) lg[i]=lg[i>>1]+1;
for(int i=1; i<=n; i++) f[i][0]=pos[i];
for(int j=1; j<=18; j++)
for(int i=1; i+(1<<j)-1<=n; i++)
f[i][j]=min(f[i][j-1], f[i+(1<<j-1)][j-1]);
for(int d=1; d<=n; d++){
int Mx=0, tim;add(d);
for(int l=1, r; l<=n; l=r+1){
r=min(n, l+d-1);
tim=query(l, r);Mx=max(Mx, tim);//求出 time(d,j) 并取前缀 max
V[Mx].push_back(d);
}
}
for(int i=1; i<=m; i++){
for(auto j : V[i]) add(j);
if(box[i].first) printf("%d/n", query(box[i].second)-query(box[i].first-1));
}
return 0;
}
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/281327.html