2020.04.11日常总结——两道codeforces

CF460C \color{green}{\text{CF460C}} CF460C

【题目翻译】: \color{blue}{\text{【题目翻译】:}} 【题目翻译】:

  • 你有 n n n 朵花,高度为 a i a_i ai,你可以浇 m m m 天的水,每天只浇一次。每次浇花的效果是让一个长度 w w w 的区间内的所有花的高度 + 1 +1 +1。问 m m m 天后最矮的花的高度最大是多少。
  • 1 ≤ n , m , w ≤ 1 × 1 0 5 , 1 ≤ a i ≤ 1 × 1 0 9 ( 1 ≤ i ≤ n ) 1 \leq n,m,w \leq 1 \times 10^5,1 \leq a_i\leq 1 \times 10^9(1 \leq i \leq n) 1n,m,w1×105,1ai1×109(1in)

【思路】: \color{blue}{\text{【思路】:}} 【思路】: 最小值最大,一看就是二分的题目。

二分 mid \text{mid} mid 表示 m m m 天后所有的花的高度都要 ≥ mid \geq \text{mid} mid。考虑如何判断(即常说的 check() 函数)。

我们可以用一个类似差分与前缀和的方法,记 T i T_i Ti 表示第 i i i 朵花被浇了 T i T_i Ti 次, C i C_i Ci 表示 T i − T i − 1 T_i-T_{i-1} TiTi1,即 T T T 数组的差分数组。

我们从前往后扫描,如果第 i i i 朵花的高度仍然 < mid < \text{mid} <mid 的话,我们就浇区间 ( i , i + w ) (i,i+w) (i,i+w)。利用差分的基本运算可以 O ( 1 ) O(1) O(1) 完成这一步。

为什么可以直接从前往后扫描呢?因为我们在第 i i i 个数后面浇花覆盖 i i i 和直接在 i i i 浇的效果一模一样。同时由于我们在处理第 i i i 个数时已经保证前面的数都 ≥ mid \geq \text{mid} mid 了,所以我们浇 i i i 时没必要再浇前面的花了(已经不影响答案了),直接贪心地浇后面即可。

时间复杂度: O ( n × log ⁡ a i ) O(n \times \log a_i) O(n×logai),空间复杂度: O ( n ) O(n) O(n)

【代码】: \color{blue}{\text{【代码】:}} 【代码】:

const int N=1e5+100;
typedef long long ll;
ll a[N],b[N],c[N<<1];
ll n,m,w,l,r,mid,ans;
inline bool check(ll mid){
	register ll x=0,cnt=0;
	memset(c,0,sizeof(c));
	for(int i=1;i<=n;i++)
		b[i]=max(mid-a[i],0ll);
	for(int i=1;i<=n;i++){
		x+=c[i];//类似于前缀和 
		if ((b[i]-=x)>0){
			if ((cnt+=b[i])>m) break;
			x+=b[i];c[i+w]-=b[i];b[i]=0;
		}
	}
	return cnt<=m;
}
int main(){
	n=read();m=read();w=read();
	for(int i=1;i<=n;i++)
		a[i]=read();
	l=0;//二分下界 
	r=5e9;//二分上界 
	while (l<=r){
		mid=(l+r)>>1;
		if (check(mid)){
			l=mid+1;//尝试更高 
			ans=mid;//更新答案 
		}
		else r=mid-1;
	}
	cout<<ans;
	return 0;
}

Codeforces gym 101502 F \color{green}{\text{Codeforces\ gym\ 101502\ F}} Codeforces gym 101502 F

题目链接

【题目大意】: \color{blue}{\text{【题目大意】:}} 【题目大意】: 现在你有一个数 x x x,初始时它为 1 1 1,你可以通过下面两种方式之一把 x x x 扩大。

  • x x x 变为 x + 1 x+1 x+1
  • x x x 变为 x × 2 x \times 2 x×2

STEP(y) \text{STEP(y)} STEP(y) 表示从 1 1 1 变成 y y y 的最少步数。给定 n n n 个数 a i a_i ai 和询问数 q q q,每次询问给定两个数 l , r l,r l,r,求:

∑ i = l r STEP( a i ) \sum\limits_{i=l}^{r} \text{STEP(} a_i) i=lrSTEP(ai)

T T T 组输入。

1 ≤ n , q ≤ 1 × 1 0 5 , 1 ≤ T ≤ 50 , 1 ≤ a i ≤ 1 × 1 0 18 ( 1 ≤ i ≤ n ) 1 \leq n,q \leq 1 \times 10^5,1 \leq T \leq 50,1 \leq a_i \leq 1 \times 10^{18}(1 \leq i \leq n) 1n,q1×105,1T50,1ai1×1018(1in)

【思路】: \color{blue}{\text{【思路】:}} 【思路】: 首先,这道题最难的是想到如何求 STEP( a i ) \text{STEP(} a_i ) STEP(ai)

我们发现一个奇数 x x x 肯定是由 x − 1 x-1 x1 通过第一中转移来的。如果 x x x 是偶数时,它可能由 x − 1 x-1 x1 x 2 \dfrac{x}{2} 2x 转移来。直觉告诉我们从 1 1 1 x 2 \dfrac{x}{2} 2x 的步数肯定比从 1 1 1 x − 1 x-1 x1 少。于是我们可以贪心地规定当 x x x 是偶数时,我们从 x 2 \dfrac{x}{2} 2x 转移到 x x x

我们惊喜地发现:这样是对的!!!于是我们可以 O ( log ⁡ a i ) O(\log a_i) O(logai) 的求 STEP ( a i ) \text{STEP} (a_i) STEP(ai)!!!

最后一个问题,如果直接求每次的输出会 TLE。很简单,前缀和就可以了!!!

【代码】: \color{blue}{\text{【代码】:}} 【代码】:

#define ll long long
const int N=1e5+100;
int test_number,n,q;
ll pre[N],step[N];
int main(){
	test_number=read();
	while (test_number--){
		n=read();q=read();//输入 
		memset(pre,0,sizeof(pre));
		memset(step,0,sizeof(step));
		for(int i=1;i<=n;i++){
			register ll d=read();
			while (d!=1){
				if (d%2) step[i]++;
				step[i]++;d/=2;
			}
			pre[i]=pre[i-1]+step[i];
		}
		for(int i=1,u,v;i<=q;i++){
			u=read();v=read();//输入询问 
			print(pre[v]-pre[u-1],'\n');//输出函数
		}
	}
	return 0;
}

你可能感兴趣的:(差分与前缀和,二分答案,贪心)