牛客小白月赛111 E 构造矩形

E 构造矩形

原题链接:https://ac.nowcoder.com/acm/contest/102742/E

思路:

这种询问方案数或者“价值”的题,通常解法要么是维护前缀信息用公式递推求解,要么是正难则反先得到全局再减去不合法的情况。在这里显然是属于前者,那么就可以分情况考虑,不妨固定 \(i\)\(ai\) ,取在 \(ai\) 上的边长为 \(t\),考虑前缀位置 \(j\) 处的 \(aj\) 对答案的贡献。

共有两种情况:

(1). \(ai-aj-t=k\),即以 \(ai-aj\) 为长,所以有:\(t=ai-aj-k\)\(1≤t≤m\),所以能够对答案产生贡献的 \(aj\) 要满足:\(ai-k-m≤aj≤ai-k-1\)。并且注意到,对于固定的 \(t\) 那么 \(aj\) 也是固定的,并且方案数为:\(m-t+1\),所以对于固定的 \(aj\),对方案的贡献为:\(m+aj+k+1-ai\)。在 \(i\) 处对答案的贡献即为:\(\sum{(aj)} + cnt(aj)*(m+k+1-ai)\)

(2). \(t-(ai-aj)=k\),即以 \(ai-aj\) 为宽,所以有:\(t=ai-aj+k\)\(k<t≤m\),所以能对答案产生贡献的 \(aj\) 要满足:\(ai+k-m≤aj<ai\)。与上面同理:当 \(t\) 固定时,\(aj\) 也固定,并且方案数为:\(m-t+1\),所以对于固定的 \(aj\),对方案的贡献为:\(m+aj+1-ai-k\)。在 \(i\) 处对答案的贡献即为:\(\sum{(aj)} + cnt(aj)*(m+1-ai-k)\)

最终类似滑动窗口用三个队列维护一下前缀,及其队列中相应位置处 \(aj\) 的总和即可,其余的值均为已知并且 \(cnt\) 值即为队列中位置的个数。

时间复杂度:\(O(n)\)

代码:

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

const int N=2e5+10;

ll n,m,k;
ll a[N];

int main(){
  cin.tie(0)->sync_with_stdio(false);
  
  cin>>n>>m>>k;
  for(int i=1;i<=n;i++) cin>>a[i];
  
  queue<int> q1,q2;
  ll sq=0;
  
  queue<int> p;
  ll sp=0;
  
  ll ans=0;
  for(int i=1;i<=n;i++){
    while(!q1.empty()){
      auto j=q1.front();
      if(a[j]>=a[i]-m-k) break;
      sq-=a[j];
      q1.pop();
    }
    
    while(!q2.empty()){
      auto j=q2.front();
      if(a[j]>a[i]-k-1) break;
      sq+=a[j];
      q1.push(j);
      q2.pop();
    } 
    
    while(!p.empty()){
      auto j=p.front();
      if(a[j]>=a[i]+k-m) break;
      sp-=a[j];
      p.pop();
    }
    
    ans+=sq+(ll)q1.size()*((ll)m+k+1-a[i]);
    ans+=sp+(ll)p.size()*((ll)m+1-k-a[i]);
    
    q2.push(i);
    p.push(i);
    sp+=a[i];
  }
  
  cout<<ans;
}
posted @ 2025-03-08 13:36  宋佳奇  阅读(52)  评论(0)    收藏  举报