- 你有 朵花,高度爲 ,你可以澆 天的水,每天只澆一次。每次澆花的效果是讓一個長度 的區間內的所有花的高度 。問 天后最矮的花的高度最大是多少。
- 。
最小值最大,一看就是二分的題目。
二分 表示 天后所有的花的高度都要 。考慮如何判斷(即常說的 check()
函數)。
我們可以用一個類似差分與前綴和的方法,記 表示第 朵花被澆了 次, 表示 ,即 數組的差分數組。
我們從前往後掃描,如果第 朵花的高度仍然 的話,我們就澆區間 。利用差分的基本運算可以 完成這一步。
爲什麼可以直接從前往後掃描呢?因爲我們在第 個數後面澆花覆蓋 和直接在 澆的效果一模一樣。同時由於我們在處理第 個數時已經保證前面的數都 了,所以我們澆 時沒必要再澆前面的花了(已經不影響答案了),直接貪心地澆後面即可。
時間複雜度:,空間複雜度:。
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;
}
現在你有一個數 ,初始時它爲 ,你可以通過下面兩種方式之一把 擴大。
- 把 變爲
- 把 變爲
記 表示從 變成 的最少步數。給定 個數 和詢問數 ,每次詢問給定兩個數 ,求:
共 組輸入。
。
首先,這道題最難的是想到如何求 。
我們發現一個奇數 肯定是由 通過第一中轉移來的。如果 是偶數時,它可能由 或 轉移來。直覺告訴我們從 到 的步數肯定比從 到 少。於是我們可以貪心地規定當 是偶數時,我們從 轉移到 。
我們驚喜地發現:這樣是對的!!!於是我們可以 的求 !!!
最後一個問題,如果直接求每次的輸出會 TLE
。很簡單,前綴和就可以了!!!
#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;
}