2020.04.11日常總結——兩道codeforces

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

【題目翻譯】:\color{blue}{\text{【題目翻譯】:}}

  • 你有 nn 朵花,高度爲 aia_i,你可以澆 mm 天的水,每天只澆一次。每次澆花的效果是讓一個長度 ww 的區間內的所有花的高度 +1+1。問 mm 天后最矮的花的高度最大是多少。
  • 1n,m,w1×105,1ai1×109(1in)1 \leq n,m,w \leq 1 \times 10^5,1 \leq a_i\leq 1 \times 10^9(1 \leq i \leq n)

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

二分 mid\text{mid} 表示 mm 天后所有的花的高度都要 mid\geq \text{mid}。考慮如何判斷(即常說的 check() 函數)。

我們可以用一個類似差分與前綴和的方法,記 TiT_i 表示第 ii 朵花被澆了 TiT_i 次,CiC_i 表示 TiTi1T_i-T_{i-1},即 TT 數組的差分數組。

我們從前往後掃描,如果第 ii 朵花的高度仍然 <mid< \text{mid} 的話,我們就澆區間 (i,i+w)(i,i+w)。利用差分的基本運算可以 O(1)O(1) 完成這一步。

爲什麼可以直接從前往後掃描呢?因爲我們在第 ii 個數後面澆花覆蓋 ii 和直接在 ii 澆的效果一模一樣。同時由於我們在處理第 ii 個數時已經保證前面的數都 mid\geq \text{mid} 了,所以我們澆 ii 時沒必要再澆前面的花了(已經不影響答案了),直接貪心地澆後面即可。

時間複雜度:O(n×logai)O(n \times \log a_i),空間複雜度: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}}

題目鏈接

【題目大意】:\color{blue}{\text{【題目大意】:}} 現在你有一個數 xx,初始時它爲 11,你可以通過下面兩種方式之一把 xx 擴大。

  • xx 變爲 x+1x+1
  • xx 變爲 x×2x \times 2

STEP(y)\text{STEP(y)} 表示從 11 變成 yy 的最少步數。給定 nn 個數 aia_i 和詢問數 qq,每次詢問給定兩個數 l,rl,r,求:

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

TT 組輸入。

1n,q1×105,1T50,1ai1×1018(1in)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)

【思路】:\color{blue}{\text{【思路】:}} 首先,這道題最難的是想到如何求 STEP(ai)\text{STEP(} a_i )

我們發現一個奇數 xx 肯定是由 x1x-1 通過第一中轉移來的。如果 xx 是偶數時,它可能由 x1x-1x2\dfrac{x}{2} 轉移來。直覺告訴我們從 11x2\dfrac{x}{2} 的步數肯定比從 11x1x-1 少。於是我們可以貪心地規定當 xx 是偶數時,我們從 x2\dfrac{x}{2} 轉移到 xx

我們驚喜地發現:這樣是對的!!!於是我們可以 O(logai)O(\log a_i) 的求 STEP(ai)\text{STEP} (a_i)!!!

最後一個問題,如果直接求每次的輸出會 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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章