HDU 5471 Count the Grid【容斥原理】

HDU5471

题意:给一个h*w(h,w<=10000)的二维矩阵,给定其中n(n<=10)个子矩阵的最大值v(v<=1000),求组成这个矩阵的方案数。

分析:由于h*w太大,而n,v比较小,此题可以从n,v下手。首先将大矩形离散化,就把大矩形分成啦数量很少的小矩形。对子矩阵的重叠部分,如果最大值不同,自然是取较小值。如果最大值相同,则比较麻烦,这时解题关键,需要通过容斥原理求方案数。例如:子矩阵a和子矩阵b最大值相等为v0,有重叠部分,求满足情况比较麻烦,可以求不满足情况。不满足情况为a不满足方案+b不满足方案-a和b同时不满足方案数。(基于ab的矩形内的数都小于等于v0)ans=总方案数-不满足情况,a不满足情况为((v0-1)^(num[a])*(v0)^(num[a并b]-num[a]) ),由此可以推至多个,用状压存储状态。所以就要分别求最大值相等的小矩形的总方案数。最后答案是所有方案数相乘。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL MOD=1e9+7;
struct node
{
    int x1,y1,x2,y2,v;
    int siz()  //返回面积
    {
        return (x2-x1)*(y2-y1);
    }
} a[20];
LL POW(LL x,LL n,LL MOD)
{
    LL ret=1;
    while(n)
    {
        if(n&1)ret=ret*x%MOD;
        x=x*x%MOD;
        n>>=1;
    }
    return ret;
}
bool compare(const node&a,const node&b)
{
    return a.x1<=b.x1&&a.y1<=b.y1&&a.x2>=b.x2&&a.y2>=b.y2;
}
int X[30],Y[30],V[30],id[30];
int jiao[12][4810],bing[12][4810];
vector<int>g[30];
int main()
{

    int TA,h,w,m,n,hy,hx,hv,cas=1;
    scanf("%d",&TA);
    while(TA--)
    {
        memset(jiao,0,sizeof(jiao));
        memset(bing,0,sizeof(bing));
        scanf("%d%d%d%d",&h,&w,&m,&n);
        hx=hy=hv=0;
        X[++hx]=0;
        X[++hx]=w;
        Y[++hy]=0;
        Y[++hy]=h;
        for(int i=1; i<=n; i++)
        {
            scanf("%d%d%d%d%d",&a[i].x1,&a[i].y1,&a[i].x2,&a[i].y2,&a[i].v);
            a[i].x1--;
            a[i].y1--;
            X[++hx]=a[i].x1;
            X[++hx]=a[i].x2;
            Y[++hy]=a[i].y1;
            Y[++hy]=a[i].y2;
            V[++hv]=a[i].v;
        }

        sort(X+1,X+hx+1);
        sort(Y+1,Y+hy+1);
        sort(V+1,V+hv+1);

        hx=unique(X+1,X+hx+1)-X-1;
        hy=unique(Y+1,Y+hy+1)-Y-1;
        hv=unique(V+1,V+hv+1)-V-1;

        for(int i=1; i<=n; i++)
        {
            a[i].v=lower_bound(V+1,V+hv+1,a[i].v)-V;
            id[i]=(int)g[a[i].v].size();
            g[a[i].v].push_back(i);
        }
        int tm,siz;
        LL all,out=0;
        for(int i=1; i<hx; i++)  //将小矩形归类
            for(int j=1; j<hy; j++)
            {
                node tem=(node)
                {
                    X[i],Y[j],X[i+1],Y[j+1],hv+1
                };
                tm=0;
                for(int k=1; k<=n; k++)
                    if(compare(a[k],tem))
                    {
                        if(a[k].v<tem.v)
                        {
                            tem.v=a[k].v;
                            tm=1<<id[k];
                        }
                        else if(a[k].v==tem.v)
                        {
                            tm|=(1<<id[k]);
                        }
                    }
                siz=tem.siz();
                if(tem.v>hv)
                    out+=siz;
                else
                    for(int k=tm; k; k=tm&(k-1))jiao[tem.v][k]+=siz;
            }

        
        for(int i=1; i<=hv; i++)  //第一次容斥,求每个最大值所包含每种状态的交集大小
        {
            int tm=1<<(int)g[i].size();
            for(int j=1; j<tm; j++)
            {
                for(int k=j; k; k=j&(k-1))
                    if(__builtin_popcount(k)&1)
                        bing[i][j]+=jiao[i][k];
                    else
                        bing[i][j]-=jiao[i][k];
            }
        }
        LL sum,ans=POW((LL)m,out,MOD);
        for(int i=1; i<=hv; i++) //第二次容斥,求方案数
        {
            int tm=(int)g[i].size();
            tm=(1<<tm);
            all=bing[i][tm-1];
            sum=0;
            for(int j=0; j<tm; j++)
                if(__builtin_popcount(j)&1)
                    sum=(POW((LL)V[i]-1,(LL)bing[i][j],MOD)*POW((LL)V[i],all-bing[i][j],MOD)*(-1)+sum)%MOD;
                else
                    sum=(POW((LL)V[i]-1,(LL)bing[i][j],MOD)*POW((LL)V[i],all-bing[i][j],MOD)+sum)%MOD;
            ans=ans*sum%MOD;
        }
        ans=(ans+MOD)%MOD;
         printf("Case #%d: %lld\n",cas++,ans);
        for(int i=0;i<=hv;i++)g[i].clear();
    }
    return 0;
}



猜你喜欢

转载自blog.csdn.net/m0_37953323/article/details/80097629