zoukankan      html  css  js  c++  java
  • 【BZOJ3235】好方的蛇(AHOI2013)-DP+单调栈+容斥

    测试地址:好方的蛇
    做法:本题需要用到DP+单调栈+容斥。
    首先,容易想到预处理出某个顶点(左上、左下、右上、右下)为(i,j)的合法矩形数目。怎么预处理呢?以处理右下顶点为(i,j)的合法矩形数目为例,我们要数的实际上是合法的左上顶点的数目,我们发现这个数目可以这样计算:令up(i,j)为从点(i,j)向上最长的连续黑色段长度,则我们需要枚举左上端点所在列k,累加min{up(i,p)|kpj}。发现这个东西可以用单调栈维护求出,因此我们就能O(n2)处理出上述东西了。
    然后就是如何求解的问题了。易知,两个不相交的矩形必定要么被一条横线分隔,要么被一条竖线分隔。那么我们对于每个合法的矩形,累加在它下边或右边的矩形数目即可,这显然可以用前缀和优化到O(n2)
    但这样有一个问题,那就是当两个矩形既能被一条横线分隔,又能被一条竖线分隔,那么这种情况会被算两次,我们要减去这种情况的贡献。于是我们枚举在上面的那个矩形,对每个矩形,减去右上顶点在这个矩形左下顶点的左下方的矩形数目,再减去左上顶点在这个矩形右下顶点的右下方的矩形数目即可。这也能用前缀和优化成O(n2),于是我们就在O(n2)的时间内完成了这道题。
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll mod=10007;
    int n;
    ll st[1010],h[1010],top;
    ll up[1010][1010]={0},down[1010][1010]={0};
    ll lu[1010][1010]={0},ld[1010][1010]={0},ru[1010][1010]={0},rd[1010][1010]={0};
    char s[1010][1010];
    
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%s",&s[i][1]);
    
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if (s[i][j]=='B') up[i][j]=up[i-1][j]+1;
                else up[i][j]=0;
            }
        }
        for(int i=n;i>=1;i--)
        {
            for(int j=1;j<=n;j++)
            {
                if (s[i][j]=='B') down[i][j]=down[i+1][j]+1;
                else down[i][j]=0;
            }
        }
    
        for(int i=1;i<=n;i++)
        {
            top=0;
            for(int j=1;j<=n;j++)
            {
                ll cnt=1,ans=lu[i][j-1]+up[i][j];
                while(top&&st[top]>up[i][j])
                {
                    ans-=h[top]*(st[top]-up[i][j]);
                    cnt+=h[top];
                    top--;
                }
                if (!top||st[top]!=up[i][j]) st[++top]=up[i][j],h[top]=cnt;
                else h[top]+=cnt;
                lu[i][j]=ans;
            }
        }
        for(int i=1;i<=n;i++)
        {
            top=0;
            for(int j=n;j>=1;j--)
            {
                ll cnt=1,ans=ru[i][j+1]+up[i][j];
                while(top&&st[top]>up[i][j])
                {
                    ans-=h[top]*(st[top]-up[i][j]);
                    cnt+=h[top];
                    top--;
                }
                if (!top||st[top]!=up[i][j]) st[++top]=up[i][j],h[top]=cnt;
                else h[top]+=cnt;
                ru[i][j]=ans;
            }
        }
        for(int i=n;i>=1;i--)
        {
            top=0;
            for(int j=1;j<=n;j++)
            {
                ll cnt=1,ans=ld[i][j-1]+down[i][j];
                while(top&&st[top]>down[i][j])
                {
                    ans-=h[top]*(st[top]-down[i][j]);
                    cnt+=h[top];
                    top--;
                }
                if (!top||st[top]!=down[i][j]) st[++top]=down[i][j],h[top]=cnt;
                else h[top]+=cnt;
                ld[i][j]=ans;
            }
        }
        for(int i=n;i>=1;i--)
        {
            top=0;
            for(int j=n;j>=1;j--)
            {
                ll cnt=1,ans=rd[i][j+1]+down[i][j];
                while(top&&st[top]>down[i][j])
                {
                    ans-=h[top]*(st[top]-down[i][j]);
                    cnt+=h[top];
                    top--;
                }
                if (!top||st[top]!=down[i][j]) st[++top]=down[i][j],h[top]=cnt;
                else h[top]+=cnt;
                rd[i][j]=ans;
            }
        }
    
        for(int i=n;i>=1;i--)
            for(int j=n;j>=1;j--)
            {
                if (rd[i][j]>0)
                    rd[i][j]=rd[i+1][j]+rd[i][j+1]-rd[i+1][j+1]+rd[i][j]-1ll;
                else rd[i][j]=rd[i+1][j]+rd[i][j+1]-rd[i+1][j+1];
            }
        for(int i=n;i>=1;i--)
            for(int j=1;j<=n;j++)
            {
                if (ld[i][j]>0)
                    ld[i][j]=ld[i+1][j]+ld[i][j-1]-ld[i+1][j-1]+ld[i][j]-1ll;
                else ld[i][j]=ld[i+1][j]+ld[i][j-1]-ld[i+1][j-1];
            }
        ll ans=0;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if (lu[i][j]>0) ans+=(lu[i][j]-1ll)*(rd[1][j+1]+rd[i+1][1]-rd[i+1][j+1]);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if (ru[i][j]>0) ans-=(ru[i][j]-1ll)*ld[i+1][j-1];
        printf("%lld",ans%mod);
    
        return 0;
    }
  • 相关阅读:
    1058 A+B in Hogwarts (20)
    1036. Boys vs Girls (25)
    1035 Password (20)
    1027 Colors in Mars (20)
    1009. Product of Polynomials (25)
    1006. Sign In and Sign Out
    1005 Spell It Right (20)
    1046 Shortest Distance (20)
    ViewPager页面滑动,滑动到最后一页,再往后滑动则执行一个事件
    IIS7.0上传文件限制的解决方法
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793289.html
Copyright © 2011-2022 走看看