【题解】[Ynoi2015] 我回来了

魔怔:

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

珂朵莉,欢迎回来

题目分析:

所谓的期望乘以 RL+1 其实就是求亵渎的触发次数,因为我们能选择的伤害只有 RL+1 种。
有一个很显然的转化,就是对于伤害 d,我们以随从的血量为下标,将这个序列划分为 [1,d],[d+1,2×d],,[d×nd+1,n]
而我们只会有 O(nlogn) 个划分出来的区间,就是一个调和级数。
可以发现若对于前 i 段,使得每一段的血量里都至少有一个随从满足条件,那么伤害 d 就可以触发 i 次亵渎。因为这个题不强制在线,所以只要我们可以预处理出来 gd,i 表示伤害 d 能触发的亵渎次数为 i 的最早时间(这里的时间就是指的操作顺序),那么只要在 gd,i 之后伤害 d 都会至少触发 i 次。
很显然,只要我们可以预处理出这个东西,就一定可以解决这个问题。
td,i 表示伤害 d 的第 i 段存在一个随从的最早时间,那么就有:

gd,i=max(gd,i1,td,i)

那么 td,i 就很简单了,设 ai 表示血量为 i 的随从的最早出现时间,则:

td,i=mind×(i1)+1xd×iax

直接一个 st 表就好了,那么我们就可以快速求出 gd,i 了,那么考虑怎么用 gd,i 去解决问题呢。
其实是很简单的,就是当我们每一次达到某个 gd,i 的值的时候就将伤害 d 产生的贡献加 1,这样到第 i 个的时候就可以使得贡献加 i 了,这个显然就可以使用树状数组维护了。当然我们也可以每次到达某一个 gd,i 就将伤害 d 的贡献设为 i,这样也是可以的。
因为我们是要按照时间顺序遍历,所以每一次都全访问一遍 gd,i 就很不现实,所以就可以考虑把 gd,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 @   linyihdfj  阅读(57)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示