P3813 [FJOI2017]矩阵填数

传送门

矩阵很大,但是发现 $n$ 很小,从这边考虑,对于一个一堆小矩阵放在一起的情况

考虑把每一块单独考虑然后方案再乘起来

但是这些奇怪的东西很不好考虑

所以暴力一点,直接拆成一个个小块

但是这样我们还要考虑到小矩形的限制,设 $f[i][S]$ 表示现在考虑第 $i$ 个小块,小矩形的限制满足的状态为 $S$ 时的方案数

发现这些小块不会跨过矩形,维护每个小块的限制(即这个块能填的最大的数)$Mx$,以及这个小块填最大数时,能使哪些小矩形满足限制 ($P$)

设小块的面积为 $S$,那么如果下一小矩形不填最大数,则转移到 $f[i+1][S]$,贡献方案数为 $(Mx[i+1]-1)^{S[i+1]}$

如果下一小矩形填最大数,则转移到 $f[i+1][S|P[i+1]]$,贡献为总方案数-不填最大数的方案数$Mx[i+1]^{S[i+1]}\ -\ (Mx[i+1]-1)^{S[i+1]}$

然后就是奇奇怪怪的离散化和预处理了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<‘0‘||ch>‘9‘) { if(ch==‘-‘) f=-1; ch=getchar(); }
    while(ch>=‘0‘&&ch<=‘9‘) { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
const int N=2007,mo=1e9+7;
int T,h,w,n,m;
int X1[N],X2[N],Y1[N],Y2[N],v[N];
int xp[N],yp[N],tx,ty,tot;
int tmp[N],t;
int f[N][N],S[N],Mx[N],P[N];
inline bool pd(int x,int y,int k) { return x>=X1[k]&&x<=X2[k]&&y>=Y1[k]&&y<=Y2[k]; }
//判断以(x,y)右上角的小块是否在矩形k中,因为离散化后小块不可能跨过矩形所以可以这样判断
inline int ksm(int x,int y)
{
    int res=1;
    while(y)
    {
        if(y&1) res=1ll*res*x%mo;
        x=1ll*x*x%mo; y>>=1;
    }
    return res;
}
int main()
{
    //以下默认往右为大,往上为大
    T=read();
    while(T--)
    {
        memset(f,0,sizeof(f)); memset(P,0,sizeof(P));
        xp[tx=1]=0; yp[ty=1]=0; tot=0;
        h=read(),w=read(),m=read(),n=read();
        for(int i=1;i<=n;i++)
        {
            X1[i]=read(),Y1[i]=read(),X2[i]=read(),Y2[i]=read(),v[i]=read();
            xp[++tx]=X1[i]-1,yp[++ty]=Y1[i]-1;//注意-1,边界很重要,左下弄成开区间很重要!
            xp[++tx]=X2[i],yp[++ty]=Y2[i];
        }
        xp[++tx]=h; yp[++ty]=w;
        sort(xp+1,xp+tx+1); sort(yp+1,yp+ty+1);
        for(int i=1;i<=tx;i++) tmp[i]=xp[i]; t=tx; tx=0;
        for(int i=1;i<=t;i++) if(i==1||tmp[i]!=tmp[i-1]) xp[++tx]=tmp[i];//离散化
        for(int i=1;i<=ty;i++) tmp[i]=yp[i]; t=ty; ty=0;
        for(int i=1;i<=t;i++) if(i==1||tmp[i]!=tmp[i-1]) yp[++ty]=tmp[i];//离散化
        for(int i=2;i<=tx;i++)
            for(int j=2;j<=ty;j++)
            {
                tot++; Mx[tot]=m;
                S[tot]=(xp[i]-xp[i-1])*(yp[j]-yp[j-1]);//小的边界是不包含的,即区间是左开右闭的,下开上闭的
                for(int k=1;k<=n;k++)
                    if(pd(xp[i],yp[j],k)) Mx[tot]=min(Mx[tot],v[k]);//处理Mx
                for(int k=1;k<=n;k++)
                    if(pd(xp[i],yp[j],k) && Mx[tot]==v[k])//处理P
                        P[tot]|=(1<<k-1);
            }
        int mx=(1<<n)-1; f[0][0]=1;//DP
        for(int i=0;i<tot;i++)
        {
            int t1=ksm(Mx[i+1]-1,S[i+1]),t2=(ksm(Mx[i+1],S[i+1])-t1+mo)%mo;
            for(int j=0;j<=mx;j++)
            {
                if(!f[i][j]) continue;
                f[i+1][j|P[i+1]]=(f[i+1][j|P[i+1]]+1ll*f[i][j]*t2%mo)%mo;//此块填最大数
                f[i+1][j]=(f[i+1][j]+1ll*f[i][j]*t1%mo)%mo;//此块不填最大数
            }
        }
        printf("%d\n",f[tot][mx]);
    }
}

原文地址:https://www.cnblogs.com/LLTYYC/p/10630950.html

时间: 2024-10-07 20:29:01

P3813 [FJOI2017]矩阵填数的相关文章

[BZOJ5010][FJOI2017]矩阵填数(状压DP)

5010: [Fjoi2017]矩阵填数 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 90  Solved: 45[Submit][Status][Discuss] Description 给定一个 h*w 的矩阵,矩阵的行编号从上到下依次为 1..h,列编号从左到右依次1..w.在这个矩阵中你需要在每 个格子中填入 1..m 中的某个数.给这个矩阵填数的时候有一些限制,给定 n 个该矩阵的子矩阵,以及该子矩阵的 最大值 v,要求你所填的方案满

bzoj5010: [Fjoi2017]矩阵填数

Description 给定一个 h*w 的矩阵,矩阵的行编号从上到下依次为 1..h,列编号从左到右依次1..w.在这个矩阵中你需要在每 个格子中填入 1..m 中的某个数.给这个矩阵填数的时候有一些限制,给定 n 个该矩阵的子矩阵,以及该子矩阵的 最大值 v,要求你所填的方案满足该子矩阵的最大值为 v.现在,你的任务是求出有多少种填数的方案满足 n 个限 制.两种方案是不一样的当且仅当两个方案至少存在一个格子上有不同的数.由于答案可能很大,你只需要输出答 案 对 1,000,000,007

FJOI2017 矩阵填数

题目 给定一个 \(h\times w\) 的矩阵,每个格子中将填入 \(1\) 到 \(m\) 中的某个整数. 一个合法的填数方案须满足 \(n\) 条限制,每条限制形如"以 \((x_1,y_1)\) 为左上角,\((x_2,y_2)\) 为右下角的子矩阵中,最大值必须为 \(v\)". 求填数方案数,对大质数取模. 把这 \(n\) 条限制按照 \(v\) 从小到大排序. 这样,就可以对每个 \(x\) 求出最小限制为 \(x\) 的区域的答案,最后处理没被限制的区域(这显然是

HihoCoder 1480:矩阵填数 (杨氏矩阵 || 钩子公式 + 筛逆元)

描述 小Hi在玩一个游戏,他需要把1, 2, 3, ... NM填入一个N行M列的矩阵中,使得矩阵每一行从左到右.每一列从上到下都是递增的. 例如如下是3x3的一种填法: 136 247 589 给定N和M,小Hi希望知道一共有多少种不同的填法. 输入 一行包含两个整数N和M. 对于60%的数据 1 <= N <= 2, 1 <= M <= 100000 对于20%的数据 N = 3, 1 <= M <= 100 对于100%的数据 1 <= N <= 3,

【算法】矩阵填数,深度优先搜索(DFS),Pascal改C语言

面向对象的上机实验 题目 以下列方式向 5*5 矩阵中填入数字.设数字i(1=<i<=25),则数字i+1 的坐标位置应为(E, W).(E, W)可根据下列关系由(x,y)算出: 1)(E, W)=(x±3,y) 2)(E, W)=(x,y±3) 3)(E, W)=(x±2,y±2) 求解问题如下: 编写一个程序,当数字1被指定于某个起始位置时,列举出其它24个数字应在的位置:列举该条件下的所有可能方案. 参考答案 网上搜索到数学奥赛中本题的Pascal代码 来自http://blog.si

【算法】【模拟法】--- 螺旋矩阵填数

[题目描述] 在一个n * n的矩阵中按照螺旋样式填入从1一直到n * n的一串整数,下面是当n = 4时的矩阵: 1 2 3 4 12 13 14 5 11 16 15 6 10 9 8 7 现在给出矩阵的边长n,直接输出该矩阵. [输入] 一个整数,即矩阵的边长n.(n <= 100) [输出] 该矩阵的所有元素. 我的分析: 使用模拟法. 设置一个结构体move,分别包含当前的坐标(x,y).当前的元素编号(score)以及当前的层数(c):再设计一个结构体first,表示每一层首元素的坐

蛇形矩阵填数

我们根据4*4矩阵找一下规律: 10 11 12 1 9 16 13 2 8 15 14 3 7 6 5 4观察可知 以右上角的一开始,可将 #include<cstdio>using namespace std;int main(){ int n,p,q,x=1; scanf("%d",&n); p=1; q=n; int a[n+2][n+2]; for(int m=n-1;m>0;m-=2){ for(int k=1;k<=4;k++){ if(k

每日一练第7天:蛇形填数

蛇形填数.在n×n方阵里填入1,2,...,n×n,要求填成蛇形. 例如,n=4时方阵为: 10 11 12  1 9 16 13  2 8 15 14  3 7   6   5  4 解决此题的一个重要原则就是先判断下一个要填的位置是否满足条件,再填数.不是发现了不能填再退回来. 代码如下: 1 #include <stdio.h> 2 #include <string.h> 3 #define MAXN 100 4 int matrix[MAXN][MAXN]; 5 6 int

python版蛇形填数

引 入 蛇形填数,一道经典有趣的算法入门题.这里用python来实现. 代码 vim snake.py #!/usr/bin/env python #-*- coding: utf-8 -*- #矩阵初始化函数 def genMatrix(rows,cols):       #用二维数组来代表矩阵     matrix = [[0 for col in range(cols)] for row in range(rows)]       for i in range(rows):