CF1402A/CEOI2020 Fancy Fence 单调栈+计数
题意:
给定一个由若干矩形从左到右拼接组成的平面图,求有多少个子矩形
题解:
对于一块\(w*h\)矩形,其子矩形数量为\(\frac{w(w+1)}{2}*\frac{h(h+1)}{2}\)
设\(f(x)=\frac{x(x+1)}{2}\)
则
\[ans=\sum_{i=1}^{n} f(w_i)*f(h_i)+\sum_{1\le i < j \le n} w_i*w_j*f(min_{k=l}^{r}(h_k))
\]
前半段式子表示在一块矩形内的子矩形数量,后半段式子表示跨越至少两块矩形的子矩形数量
前半段可以\(o(n)\)枚举计算,考虑后半段如何计算
可以观察到\(min(h_k)\)在一段区间里是一样的,可以利用这个性质进行加速计算
我们枚举\(min(h_k)\),即枚举每一个\(h_i\)其作为\(min\)时影响的区间
则可以用一个单调栈找到\(l_i,r_i\)表示\(h_i\)作为\(min\)时影响的区间
对于一个区间\([l_i,r_i]\),它对答案的贡献分为三部分:左右端点在\(i\)块外,左右端点其中一个在\(i\)块内
即
\[f(h_i)*\sum_{j=l_i}^{j<i} w_j*\sum_{k=i+1}^{r_i} w_k
\]
\[f(h_i)*w_i*\sum_{j=l_i}^{j<i} w_j
\]
\[f(h_i)*w_i*\sum_{k=i+1}^{r_i} w_k
\]
这里注意当\(h_i==h_j\)时,对影响区间需要细节判断
如果\(l_i,r_i\)都包含了相等的情况,那么会重复计算;如果都不包含,则会缺少计算。
因此需要一开一闭,即\(l_i\)包含相等,\(r_i\)不包含,此时在计算答案时刚好不会重复计算也不会缺少计算
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#include<string>
#include<map>
#include<stack>
#define mod 1000000007
#define ll long long
using namespace std;
ll n;
ll h[100101],w[100101],pre[100101],l[100101],r[100101];
ll st[100101],stn;
ll ans;
ll f(ll x){return x*(x+1)/2%mod;}
void solve()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++)scanf("%lld",&h[i]);
for(int i=1;i<=n;i++){scanf("%lld",&w[i]);pre[i]=(w[i]+pre[i-1])%mod;}
stn=1;st[stn]=1;
for(int i=2;i<=n;i++)
{
while(stn && h[st[stn]]>h[i])r[st[stn--]]=i-1;
st[++stn]=i;
}
while(stn)r[st[stn--]]=n;
stn=1;st[stn]=n;
for(int i=n-1;i>=1;i--)
{
while(stn && h[st[stn]]>=h[i])l[st[stn--]]=i+1;
st[++stn]=i;
}
while(stn)l[st[stn--]]=1;
//for(int i=1;i<=n;i++)printf("%d ",l[i]);printf("\n");
// for(int i=1;i<=n;i++)printf("%d ",r[i]);printf("\n");
for(int i=1;i<=n;i++)
{
ans=(ans+f(h[i])*f(w[i])%mod)%mod;
ans=(ans+f(h[i])*(pre[i-1]-pre[l[i]-1]+mod)%mod*(pre[r[i]]-pre[i]+mod)%mod)%mod;
ans=(ans+f(h[i])*w[i]%mod*(pre[i-1]-pre[l[i]-1]+mod)%mod)%mod;
ans=(ans+f(h[i])*w[i]%mod*(pre[r[i]]-pre[i]+mod)%mod)%mod;
//printf("%d %lld\n",i,ans);
}
printf("%lld\n",ans);
}
int main()
{
int T=1;
//scanf("%d",&T);
while(T--)
{
solve();
}
return 0;
}