牛客小白月赛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;
}

浙公网安备 33010602011771号