2019牛客暑期多校訓練營(第三場)G: Removing Stones(啟發式分治)


題意:給定N,表示N堆石子,每堆石子數為a[],問多少個區間,可以滿足“石子總和若為偶數,那么可以兩兩取來自不同堆的石子,直到取完; 如果為奇數,那么排除其中一個,然后可以兩兩取來自不同堆的石子,直到取完”。

思路:結論是,如果一個區間的區間和大於等於區間最大值的兩倍,則這個區間合法。 考慮分治,我們首先找到區間最大值(為了不重復統計,多個最大值時,統一取最左邊的,這個可以ST表示實現),然后考慮跨越這個位置的合法區間個數。枚舉一端,另外一段二分即可。

由於分治的性質,我們每次的復雜度要傾向於小的那邊,即是一個啟發式合並的逆過程,所以啟發式分治復雜度是O(NlogN)的,加上二分,這個做法的復雜度是O(Nlog^2N)。

可以參考差不多的題:https://www.cnblogs.com/hua-dong/p/11171241.html。所以我感覺我遇到的原題還挺多的。

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=300010;
int lg[maxn],a[maxn],dp[maxn][20],N;
ll sum[maxn],sum2[maxn],ans;
void RMQ()
{
    rep(i,1,N) dp[i][0]=i;
    for(int i=1;(1<<i)<=N;i++){
        for(int j=1;j+(1<<i)-1<=N;j++){
           dp[j][i]=a[dp[j][i-1]]>=a[dp[j+(1<<(i-1))][i-1]]?
             dp[j][i-1]:dp[j+(1<<(i-1))][i-1];
        }
    }
}
void solve(int L,int R)
{
    if(L>=R) return ;
    int k=lg[R-L+1];
    int Mid=(a[dp[L][k]]>=a[dp[R-(1<<k)+1][k]]?
      dp[L][k]:dp[R-(1<<k)+1][k]);
    if(Mid-L<R-Mid){
        rep(i,L,Mid) {
            int pos=lower_bound(sum+Mid,sum+R+1,sum[i-1]+2LL*a[Mid])-sum;
            ans+=R-pos+1;
        }
    }
    else {
        rep(i,Mid,R) {
            int pos=lower_bound(sum2+N-Mid+1,sum2+N-L+2,sum2[N-i]+2LL*a[Mid])-sum2;
            ans+=N+1-L-pos+1;
        }
    }
    solve(L,Mid-1); solve(Mid+1,R);
}
int main()
{
    lg[0]=-1;
    rep(i,1,maxn-1) lg[i]=lg[i>>1]+1;
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&N); ans=0;
        rep(i,1,N)
          scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
        rep(i,1,N) sum2[i]=sum2[i-1]+a[N+1-i];
        RMQ();
        solve(1,N);
        printf("%lld\n",ans);
    }
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM