题目分析:
所谓的期望乘以 \(R - L + 1\) 其实就是求亵渎的触发次数,因为我们能选择的伤害只有 \(R - L + 1\) 种。
有一个很显然的转化,就是对于伤害 \(d\),我们以随从的血量为下标,将这个序列划分为 \([1,d],[d+1,2\times d],\cdots,[d\times \lfloor \frac{n}{d} \rfloor+1,n]\)。
而我们只会有 \(O(n \log n)\) 个划分出来的区间,就是一个调和级数。
可以发现若对于前 \(i\) 段,使得每一段的血量里都至少有一个随从满足条件,那么伤害 \(d\) 就可以触发 \(i\) 次亵渎。因为这个题不强制在线,所以只要我们可以预处理出来 \(g_{d,i}\) 表示伤害 \(d\) 能触发的亵渎次数为 \(i\) 的最早时间(这里的时间就是指的操作顺序),那么只要在 \(g_{d,i}\) 之后伤害 \(d\) 都会至少触发 \(i\) 次。
很显然,只要我们可以预处理出这个东西,就一定可以解决这个问题。
设 \(t_{d,i}\) 表示伤害 \(d\) 的第 \(i\) 段存在一个随从的最早时间,那么就有:
那么 \(t_{d,i}\) 就很简单了,设 \(a_i\) 表示血量为 \(i\) 的随从的最早出现时间,则:
\[t_{d,i} = \min_{d \times (i-1)+1\le x \le d \times i} a_x \]直接一个 \(st\) 表就好了,那么我们就可以快速求出 \(g_{d,i}\) 了,那么考虑怎么用 \(g_{d,i}\) 去解决问题呢。
其实是很简单的,就是当我们每一次达到某个 \(g_{d,i}\) 的值的时候就将伤害 \(d\) 产生的贡献加 \(1\),这样到第 \(i\) 个的时候就可以使得贡献加 \(i\) 了,这个显然就可以使用树状数组维护了。当然我们也可以每次到达某一个 \(g_{d,i}\) 就将伤害 \(d\) 的贡献设为 \(i\),这样也是可以的。
因为我们是要按照时间顺序遍历,所以每一次都全访问一遍 \(g_{d,i}\) 就很不现实,所以就可以考虑把 \(g_{d,i}\) 放到桶里,这样就可以快速知道当前时间有哪些是需要处理的了。
代码:
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e6+5;
int n,m,sum[N],f[N][22],a[N];
pair<int,int> q[N];
vector<int> v[N];
void modify(int x){
for(int i=x; i<=n; i+=i&(-i)) sum[i]++;
}
int query(int x){
int ans = 0;
for(int i=x;i;i-=i&(-i)) ans += sum[i];
return ans;
}
int Query(int l,int r){
int len = log2(r-l+1);
return min(f[l][len],f[r - (1<<len) + 1][len]);
}
signed main(){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
scanf("%lld%lld",&n,&m);
for(int i=1; i<=n; i++) a[i] = m + 1;
for(int i=1; i<=m; i++){
int opt;scanf("%lld",&opt);
q[i] = {-1,-1};
if(opt == 1){
int x;scanf("%lld",&x);
a[x] = min(a[x],i);
}
else{
int l,r;scanf("%lld%lld",&l,&r);
q[i].first = l,q[i].second = r;
}
}
for(int i=1; i<=n; i++) f[i][0] = a[i];
for(int i=1; i<=20; i++){
for(int j=1; j + (1<<i) - 1 <=n; j++){
f[j][i] = min(f[j][i-1],f[j+(1<<(i-1))][i-1]);
}
}
for(int d=1; d<=n; d++){ //枚举 d
modify(d);
int lst = 0;
for(int j=1; j<=n; j+=d){
lst = max(lst,Query(j,min(j+d-1,n)));
v[lst].push_back(d);
}
}
for(int i=1; i<=m; i++){
for(auto x : v[i]) modify(x);
if(q[i].first != -1){
int l = q[i].first,r = q[i].second;
printf("%lld\n",query(r) - query(l-1));
}
}
return 0;
}