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;
}

posted @ 2020-10-20 22:01  worcher  阅读(170)  评论(0编辑  收藏  举报