P3160 [CQOI2012]局部极小值

题目

P3160 [CQOI2012]局部极小值

一眼就是状压,接下来就不知道了\(qwq\)

做法

我们能手玩出局部小值最多差不多是\(8,9\)个的样子,\(dp_{i,j}\)为填满\(1~i\)数字,局部小值的状态为\(j\)

第\(k\)个局部极小值填\(i\):\(dp[i][j]=(dp[i][j]+dp[i-1][j^(1<<k-1)])%p\)

不填在局部极小值,显然有些地方不能填\(i\)的,首先还没填的局部极小值不填,其周围也不能填(填\(i\)后后面再填比不符合局部极小值)
我们预处理出每种状态不能填时的位置:\(dp[i][j]=(dp[i][j]+dp[i-1][j]*max(num[j]-i+1,0))%p\)

一顿操作后发现WA了,我们得到的局部极小值可能不是恰好是给出的\(X\)(更多)

容斥原理:Ans=至少多0个极小值-至少多1个极小值+至少多两个极小值......

理解:至少多0个\((x,x+1,x+2,x+3...)-k(x+1,x+2,x+3...)\)其中\(k\)为某些值的系数
填了至少多0个极小值后,有多填的部分那就得减去至少多1个极小值,至少多1个极小值的位置也有很多,就会有一些位置减掉的系数为(k>1),就要又加上

\(dfs\)时加上能被多余的极小值填上的地方换成‘X‘,然后多次做\(dp\)

My complete code

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
const int maxn=31;
const int p=12345678;
const int dx[8]={-1,-1,-1,0,1,1,1,0},dy[8]={-1,0,1,1,1,0,-1,-1};
int n,m,tot;
LL ans;
int x[maxn],y[maxn],num[1<<9],dp[maxn][1<<9];
bool visit[maxn][maxn];
char s[maxn][maxn];
inline int Solve(){
    tot=0;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
            if (s[i][j]=='X')
                x[++tot]=i,
                y[tot]=j;
    int Up=1<<tot;
    for(int i=0;i<Up;i++){
        int cnt(0);
        memset(visit,0,sizeof(visit));
        for(int j=1;j<=tot;++j)
            if(!((i>>(j-1))&1)){
                visit[x[j]][y[j]]=1;
                for(int k=0;k<8;++k){
                    int xx=x[j]+dx[k];
                    int yy=y[j]+dy[k];
                    if(xx>0&&yy>0&&xx<=n&&yy<=m)
                        visit[xx][yy]=1;
                }
            }
        for(int j=1;j<=n;++j)
            for(int k=1;k<=m;++k)
                if(visit[j][k])
                    ++cnt;
        num[i]=n*m-cnt;
    }
    memset(dp,0,sizeof(dp));
    dp[0][0]=1;
    for(int i=1;i<=n*m;++i)
        for(int j=0;j<Up;++j){
            dp[i][j]=(dp[i][j]+dp[i-1][j]*max(num[j]-i+1,0))%p;
            for(int k=1;k<=tot;k++)
                if(j&(1<<(k-1)))
                    dp[i][j]=(dp[i][j]+dp[i-1][j^(1<<k-1)])%p;
        }
    return dp[n*m][Up-1];
}
void Dfs(int x,int y,int k){
    if(y==m+1){
        Dfs(x+1,1,k);
        return;
    }
    if(x==n+1){
        ans=(ans+(((k&1)==0)?1:-1)*Solve()+p)%p;
        return;
    }
    Dfs(x,y+1,k);
    if(s[x][y]!='X'){
        bool f=true;
        for(int i=0;i<8;i++)
            if(s[x+dx[i]][y+dy[i]]=='X'){
                f=false;
                break;
            }
        if(f){
            s[x][y]='X',
            Dfs(x,y+1,k+1),
            s[x][y]='.';
        }
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf(" %s",s[i]+1);
    Dfs(1,1,0);
    printf("%lld\n",ans);
    return 0;
}
/*
5 5
.....
...X.
.X...
.....
....X
*/

原文地址:https://www.cnblogs.com/y2823774827y/p/10262116.html

时间: 2024-09-28 13:38:57

P3160 [CQOI2012]局部极小值的相关文章

2669[cqoi2012]局部极小值 容斥+状压dp

2669: [cqoi2012]局部极小值 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 774  Solved: 411[Submit][Status][Discuss] Description 有一个n行m列的整数矩阵,其中1到nm之间的每个整数恰好出现一次.如果一个格子比所有相邻格子(相邻是指有公共边或公共顶点)都小,我们说这个格子是局部极小值. 给出所有局部极小值的位置,你的任务是判断有多少个可能的矩阵. Input 输入第一行包含两个整数

BZOJ 2669 cqoi2012 局部极小值 状压DP+容斥原理

题目大意:给定一个n?m的矩阵,标记出其中的局部极小值,要求填入1...n?m,求方案数 <多年的心头大恨终于切掉了系列> 考虑将数字从小到大一个一个填进去 由于局部极小值最多8个,我们可以状压DP 令fi,j表示已经填完了前i个数,局部极小值的填充状态为j的方案数 预处理出cntj表示填充状态为j时共有多少位置是可以填充的(包括已填充的局部极小值位置) 那么有DP方程fi,j=fi?1,j?C1cntj?i+1+∑k∈jfi?1,j?{k} 但是问题是这样虽然保证了标记的位置都是局部最小值,

[CQOI2012]局部极小值

嘟嘟嘟 谁说CQOI的题都是板儿题,我就觉得这题挺难的-- 看到数据范围这么小,就会想状压.然而\(2 ^ {28}\)肯定过不了.不过对于所有的极小值的格子,最多不会超过8个,所以我们状压选了哪些局部极小值的格子(坑儿). 然后我们从小到大填数,那么对于一个数\(i\),他无非就两种填法:填入一个坑,或是填坑以外的点. 填一个坑儿就是直接填,于是有\(dp[i][S] = \sum dp[i - 1][S\) ^ \((1 << k)]\). 不填坑的话,得考虑填上这个数之后,那些还没有填的

【bzoj2669】 cqoi2012—局部极小值

http://www.lydsy.com/JudgeOnline/problem.php?id=2669 (题目链接) 题意 给出一个$n*m$的整数矩阵,其中$[1,nm]$中的整数每个出现一次,有一些位置为局部最小值.问方案数. Solution 好神的dp啊. http://blog.csdn.net/popoqqq/article/details/48028773 $cnt_j$表示的是,在局部最小值被填充的状态为$j$的情况下,目前有多少个位置可以填,这些位置中包括已经被填了数的位置.

●BZOJ 2669 [cqoi2012]局部极小值

题链: http://www.lydsy.com/JudgeOnline/problem.php?id=2669 题解: 容斥,DP,DFS 先看看 dp 部分:首先呢,X的个数不会超过 8个.个数很少,所以考虑状压,把需要填 X的那几个位置状压为二进制10表示对应的那个X位置是否已经填数.同时填的数互不重复,考虑从小填到大. 令 cnt[S] 表示除了不在集合 S 里的 X 位置及其周围的位置,剩下的位置个数. 定义 dp[i][S]表示从小到大填数填完了i这个数,且已经填了的 S 这个集合里

bzoj2669 [cqoi2012]局部极小值 状压DP+容斥

题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=2669 题解 可以发现一个 \(4\times 7\) 的矩阵中,有局部最小值的点最多有 \(2\times 4 = 8\) 个,所以我们可以状压一下每个局部最小值的位置有没有被选. 从小到大填入每一个格子,那么如果一个点的周围有没有被填上的局部最小值,那么这个格子不可以被填.所以预处理一下每种状态下可以自由填多少格子,然后如果状态保持不变的话,就可以这样转移. 如果状态变化,就是说填了一个局

2016 CCPC 网络赛 B 高斯消元 C 树形dp(待补) G 状压dp+容斥(待补) H 计算几何

2016 CCPC 网络赛 A - A water problem 水题,但读题有个坑,输入数字长度很大.. B - Zhu and 772002 题意:给出n个数(给出的每个数的质因子最大不超过2000),选出多个数相乘得b.问有多少种选法让b 为完全平方数. tags:高斯消元,求异或方程组解的个数.   好题 每个数先素数分解开.  对于2000以内的每个素数p[i],这n个数有奇数个p[i]则系数为1,偶数个则系数为0,最后n个数的p[i]系数异或和都要为0才会使得最后的积为完全平方数.

计数类问题专题

主要是前两天被uoj的毛爷爷的题虐的不轻,心里很不爽啊,必须努力了,, 计数类问题分为:1.组合数学及数论计数 2.dp:状态压缩dp,插头轮廓线dp,树形dp,数位dp,普通dp 3.容斥原理 4.polya原理 5.图论计数 6.生成函数 7.其它(生成树计数等等) 本文主要研究前3个内容 考虑基本计数原理:加法原理,减法原理,乘法原理,除法原理 计数的基本原则:结果不重不漏 加法原理比较自然,中间过程有时减法原理 考虑到无向,有向图的各种量值(生成树之类)计数,状态压缩dp解决 论文:ht

【BZOJ2666】[cqoi2012]组装 贪心

[BZOJ2666][cqoi2012]组装 Description 数轴上有m个生产车间可以生产零件.一共有n种零件,编号为1~n.第i个车间的坐标为xi,生产第pi种零件(1<=pi<=n).你需要在数轴上的某个位置修建一个组装车间,把这些零件组装起来.为了节约运输成本,你需要最小化cost(1)+cost(2)+…+cost(n),其中cost(x)表示生产第x种零件的车间中,到组装车间距离的平方的最小值. Input 输入第一行为两个整数n, m,即零件的种类数和生产车间的个数.以下m