CF549F(分治+启发式合并)
这道题的初始思路可以看的出来
是一道分治的思想,这种题往往枚举端点计算贡献
而这一题因为有个最大值的限制,所以我们考虑维护每个点作为最大值的答案
那么一般来说,都是在区间内,枚举首位,然后二分答案,但是这样复杂度会退化,例如一个很长的递增子序列
因此我们考虑启发式合并,前缀和后缀哪边小枚举哪边,这样复杂度就是log,因此这道题套了两个log
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int,int> pll; const int N=1e6+10; const int mod=998244353; ll sum[N],a[N]; vector<int> num[N]; int q[N]; int pre[N],suf[N]; int solve(int l,int r,int x){ return upper_bound(num[x].begin(),num[x].end(),r)-lower_bound(num[x].begin(),num[x].end(),l); } int main(){ ios::sync_with_stdio(false); int n,k; cin>>n>>k; int i; num[0].push_back(0); for(i=1;i<=n;i++){ cin>>a[i]; sum[i]=sum[i-1]+a[i]; num[sum[i]%k].push_back(i); } int hh=0,tt=0; q[0]=0; for(i=1;i<=n;i++){ while(hh<=tt&&a[q[tt]]<=a[i]){ tt--; } pre[i]=q[tt]+1; q[++tt]=i; } hh=0,tt=0; q[0]=n+1; a[n+1]=1e9+1; for(i=n;i>=1;i--){ while(hh<=tt&&a[q[tt]]<a[i]){ tt--; } suf[i]=q[tt]-1; q[++tt]=i; } ll ans=-n; for(i=1;i<=n;i++){ if(suf[i]-i>i-pre[i]){ for(int j=pre[i]-1;j<i;j++){ ans=ans+solve(i,suf[i],(sum[j]+a[i])%k); } } else{ for(int j=i;j<=suf[i];j++){ int tmp=solve(pre[i]-1,i-1,(sum[j]-a[i])%k); ans+=tmp; } } } cout<<ans<<endl; return 0; }
没有人不辛苦,只有人不喊疼