BZOJ5010 FJOI2017矩阵填数(容斥原理)

  如果只考虑某个子矩阵的话,其最大值为v的方案数显然是vsize-(v-1)size。问题在于处理子矩阵间的交叉情况。

  如果两个交叉的子矩阵所要求的最大值不同,可以直接把交叉部分划给所要求的最大值较小的子矩阵。那么,所要求最大值不同的格子彼此间是独立的。于是现在可以只考虑要求相同的格子。

  直接计算似乎很麻烦。由于n很小,考虑一个很套路的容斥:至少0个不满足限制的方案数-至少1个不满足限制的方案数+至少2个不满足限制的方案数……于是我们可以枚举哪些矩阵不满足限制,剩下的随便填(当然要在所限制的最大值之内)。

  计算这些矩形的交和并也是一个有点麻烦的问题。可以离散化后暴力统计。这里离散化后应该每个位置表示一段区间比较方便,所以读入时++x2,++y2。由于数据范围实在太小怎么做都行。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
#define P 1000000007
#define N 13
#define y1 y3
#define y2 y4
int T,r,c,n,m,row[N<<1],line[N<<1],flag[N<<1][N<<1],ans,sum,nw,nv;
bool choose[N];
struct data
{
    int x1,y1,x2,y2,v,size;
    int tag[N<<1][N<<1];
    bool operator <(const data&a) const
    {
        return v>a.v;
    }
}a[N];
int ksm(int a,int k)
{
    if (k==0) return 1;
    int tmp=ksm(a,k>>1);
    if (k&1) return 1ll*tmp*tmp%P*a%P;
    else return 1ll*tmp*tmp%P;
}
int calc(int v)
{
    memset(flag,0,sizeof(flag));
    for (int i=1;i<=n;i++)
    if (a[i].v==v&&!choose[i])
        for (int j=a[i].x1;j<a[i].x2;j++)
            for (int k=a[i].y1;k<a[i].y2;k++)
            if (a[i].tag[j][k]) flag[j][k]=1;
    for (int i=1;i<=n;i++)
    if (choose[i])
        for (int j=a[i].x1;j<a[i].x2;j++)
            for (int k=a[i].y1;k<a[i].y2;k++)
            if (a[i].tag[j][k]) flag[j][k]=-1;
    int s1=0,s2=0;
    for (int i=1;i<nw;i++)
        for (int j=1;j<nv;j++)
        if (flag[i][j]==1) s1+=(row[i+1]-row[i])*(line[j+1]-line[j]);
        else if (flag[i][j]==-1) s2+=(row[i+1]-row[i])*(line[j+1]-line[j]);
    return 1ll*ksm(v,s1)*ksm(v-1,s2)%P;
}
void dfs(int k,int s,int v)
{
    if (k>n)
    {
        if (s&1) sum=(sum-calc(v)+P)%P;
        else sum=(sum+calc(v))%P;
        return;
    }
    if (a[k].v==v) choose[k]=1,dfs(k+1,s+1,v);
    choose[k]=0;dfs(k+1,s,v);
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj5010.in","r",stdin);
    freopen("bzoj5010.out","w",stdout);
    const char LL[]="%I64d\n";
#else
    const char LL[]="%lld\n";
#endif
    T=read();
    while (T--)
    {
        r=read(),c=read(),m=read(),n=read();
        for (int i=1;i<=n;i++) 
        a[i].x1=read(),a[i].y1=read(),a[i].x2=read()+1,a[i].y2=read()+1,a[i].v=read(),a[i].size=0,memset(a[i].tag,0,sizeof(a[i].tag));
        int w=0,v=0;
        for (int i=1;i<=n;i++) 
        row[++w]=a[i].x1,row[++w]=a[i].x2,line[++v]=a[i].y1,line[++v]=a[i].y2;
        row[++w]=1,row[++w]=r+1;line[++v]=1,line[++v]=c+1;
        sort(row+1,row+w+1);sort(line+1,line+v+1);
        nw=unique(row,row+w+1)-row-1,nv=unique(line,line+v+1)-line-1;
        for (int i=1;i<=n;i++)
        a[i].x1=lower_bound(row+1,row+nw+1,a[i].x1)-row,a[i].x2=lower_bound(row+1,row+nw+1,a[i].x2)-row,
        a[i].y1=lower_bound(line+1,line+nv+1,a[i].y1)-line,a[i].y2=lower_bound(line+1,line+nv+1,a[i].y2)-line;
        sort(a+1,a+n+1);
        memset(flag,0,sizeof(flag));
        for (int i=1;i<=n;i++)
            for (int j=a[i].x1;j<a[i].x2;j++)
                for (int k=a[i].y1;k<a[i].y2;k++)
                flag[j][k]=a[i].v;
        for (int i=1;i<=n;i++)
            for (int j=1;j<nw;j++)
                for (int k=1;k<nv;k++)
                if (flag[j][k]==a[i].v) a[i].size++,a[i].tag[j][k]=1;
        ans=1;
        for (int i=1;i<nw;i++)
            for (int j=1;j<nv;j++)
            if (flag[i][j]==0) ans=1ll*ans*ksm(m,(row[i+1]-row[i])*(line[j+1]-line[j]))%P;
        for (int i=1;i<=n;i++)
        {
            sum=0;int t=i;
            while (a[t+1].v==a[i].v) t++;
            memset(choose,0,sizeof(choose));
            dfs(i,0,a[i].v);
            ans=1ll*ans*sum%P;
            i=t;
        }
        cout<<ans<<endl;
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Gloid/p/9471312.html