【题解】[Ynoi2015] 我回来了

魔怔:

在太阳西斜的这个世界里,置身天上之森。 等这场战争结束后,不归之人与望眼欲穿的人们,人人本着正义之名。 长存不灭的过去,逐渐消逝的未来。 我回来了, 纵使日薄西山,即便看不到未来, 此时此刻的光辉,盼君勿忘。 ——世上最幸福的女孩

珂朵莉,欢迎回来

题目分析:

所谓的期望乘以 \(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\) 段存在一个随从的最早时间,那么就有:

\[g_{d,i} = \max(g_{d,i-1},t_{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;
}
posted @ 2023-03-08 17:04  linyihdfj  阅读(57)  评论(0编辑  收藏  举报