1. 程式人生 > >HDU 5471 Count the Grid【容斥原理】

HDU 5471 Count the Grid【容斥原理】

題意:給一個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;
}