CF1954E Chain Reaction

正在写数论分块,发现这道题竟然是向上取整的数论分块,我直接就去写了。

题意概括

\(n\) 个怪兽,对于一个伤害 \(k\),每一次会对一段连续的存活的怪物打出 \(k\) 的伤害,对于一堆 \(k\) 求出每一个 \(k\) 的最小攻击次数。

我们先考虑一下对于一个 \(k\) 而言,它的答案是什么。

可以算每一对相邻的贡献是多少,看得出来就是 \(max(0,ceil(\frac{a_i}{k})-ceil(\frac{a_{i-1}}{k}))\)

整体就是 \(\sum_{i=1}^{n}max(0,ceil(\frac{a_i}{k})-ceil(\frac{a_{i-1}}{k}))\)

这个显然是数论分块可求的。

热知识:对于 \(ceil(\frac{n}{l})\),对应的右端点是 \(floor(\frac{n-1}{floor(\frac{n-1}{l})})\)

我们感觉会做了,但是我们发现似乎没有什么好的办法清空每一次,写数据结构太麻烦,而且这个题似乎卡常?反正多了一个 log 是绝对不可能过的。

所以我们便采取新的策略,计算每一对相邻的点对于所有 \(k\) 的贡献。

我们使用差分,做数论分块就行了。

注意这个题卡常,define int long long 会 T。

代码。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
namespace BaiBaiShaFeng{
	const int MN=3e5+315;
	int n, a[MN], maxn=0;
	long long d[MN];
	void update(int l, int r, int val){
		if(val<0) return;
		d[l]+=val; d[r+1]-=val;
	}
	void solve(){
		cin>>n; d[0]=1;
		for(int i=1; i<=n; ++i) cin>>a[i];
		for(int i=1; i<=n; ++i) maxn=max(maxn,a[i]);
		for(int i=1; i<=n; ++i){
			int vala=a[i], valb=a[i-1];
			for(int l=1, r; l<=maxn; l=r+1){
				int mra, mrb;
				if(l>=vala) mra=maxn+1;
				else mra=((vala-1)/((vala-1)/l));
				if(l>=valb) mrb=maxn+1;
				else mrb=((valb-1)/((valb-1)/l));
				r=min(mra,mrb);
				if(r==maxn+1) break;
				update(l,r,(vala-1)/l-max((int)0,valb-1)/l);
			}
		}
		for(int i=1; i<=maxn; ++i) d[i]+=d[i-1];
		for(int i=1; i<=maxn; ++i) cout<<d[i]<<" ";
	}
}
signed main(){
	//ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	BaiBaiShaFeng::solve();
	return 0;
}
posted @ 2025-08-19 10:49  BaiBaiShaFeng  阅读(10)  评论(0)    收藏  举报
Sakana Widget右下角定位