BZOJ2331:[SCOI2011]地板——题解

http://www.lydsy.com/JudgeOnline/problem.php?id=2331

题面复制于洛谷

题目描述

lxhgww的小名叫”小L“,这是因为他总是很喜欢L型的东西。小L家的客厅是一个R*C的矩形,现在他想用L型的地板来铺满整个客厅,客厅里有些位置有柱子,不能铺地板。现在小L想知道,用L型的地板铺满整个客厅有多少种不同的方案?需要注意的是,如下图所示,L型地板的两端长度可以任意变化,但不能长度为0。

铺设完成后,客厅里面所有没有柱子的地方都必须铺上地板,但同一个地方不能被铺多次。

输入输出格式

输入格式:

输入的第一行包含两个整数,R和C,表示客厅的大小。接着是R行,每行C个字符。‘_‘表示对应的位置是空的,必须铺地板;‘*‘表示对应的位置有柱子,不能铺地板。

输出格式:

输出一行,包含一个整数,表示铺满整个客厅的方案数。由于这个数可能很大,只需输出它除以20110520的余数。

输入输出样例

输入样例#1:

2 2
*_
__

输出样例#1:

1

输入样例#2:

3 3
____*_
___

输出样例#2:

8

参考了:http://blog.csdn.net/regina8023/article/details/44838887

我终于可以告别插头dp啦233333……<—此人已疯

这道题的难点在于将插头dp的插头的定义进行修改。

0:无插头

1:有插头且当前格子所在的地板能再转弯。

2:有插头且当前格子所在的地板不能再转弯。

有了这些就可以按照插头dp的思想进行分情况讨论了:

(摘自参考博客)

1.00-->22 或 10 或 01

2.11-->00

3.10-->20 或 01

20-->00 或 02

4.01-->10 或 02

02-->00 或 20

最终把所有情况枚举累加即可。

PS:第二种情况的11转换成了00实质上是11相交的地方变成了这块地板的转折点(也可以理解为两块地板并在了一起)。

#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int INF=2147483647;
const int mod=300000;
const int M=1000005;
const int p=20110520;
struct node{
    int to,nxt;
}edge[M];
int head[M],cnt;
int n,m;
bool mp[105][105];
int cur,pre;
int state[2][M];
ll ans[2][M],cntt;
int tot[2];
int bit[15];
inline void getbit(){
    for(int i=1;i<15;i++)bit[i]=i<<1;
    return;
}
inline void add(int u,int v){
    cnt++;
    edge[cnt].to=v;
    edge[cnt].nxt=head[u];
    head[u]=cnt;
    return;
}
void insert(int now,ll num){
    int u=now%mod;
    for(int i=head[u];i;i=edge[i].nxt){
        int v=edge[i].to;
        if(state[cur][v]==now){
            ans[cur][v]+=num%p;
            ans[cur][v]%=p;
            return;
        }
    }
    add(u,++tot[cur]);
    state[cur][tot[cur]]=now;
    ans[cur][tot[cur]]=num%p;
    return;
}
void plugdp(){
    cur=0;
    tot[cur]=1;
    ans[cur][1]=1;
    state[cur][1]=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=tot[cur];j++){
            state[cur][j]<<=2;
        }
        for(int j=1;j<=m;j++){
            memset(head,0,sizeof(head));cnt=0;
            pre=cur,cur^=1;
            tot[cur]=0;
            for(int k=1;k<=tot[pre];k++){
                int now=state[pre][k];
                ll num=ans[pre][k]%p;
                int is_down=(now>>bit[j-1])%4;
                int is_right=(now>>bit[j])%4;
                if(!mp[i][j]){
                    if(!is_down&&!is_right)
                    insert(now,num);
                }
                else if(!is_down&&!is_right){
                    if(mp[i+1][j])
                       insert(now+(1<<bit[j-1]),num);
                    if(mp[i][j+1])
                       insert(now+(1<<bit[j]),num);
                    if(mp[i][j+1]&&mp[i+1][j])
                    insert(now+2*(1<<bit[j-1])+2*(1<<bit[j]),num);
                }
                else if(is_down&&!is_right){
                    if(is_down==1){
                        if(mp[i+1][j])insert(now+(1<<bit[j-1]),num);
                        if(mp[i][j+1])insert(now-(1<<bit[j-1])+(1<<bit[j]),num);
                    }else{
                        insert(now-2*(1<<bit[j-1]),num);
                        if(mp[i][j+1])insert(now-2*(1<<bit[j-1])+2*(1<<bit[j]),num);
                    }
                }
                else if(!is_down&&is_right){
                    if(is_right==1){
                        if(mp[i+1][j])insert(now+(1<<bit[j-1])-(1<<bit[j]),num);
                        if(mp[i][j+1])insert(now+(1<<bit[j]),num);
                    }else{
                        insert(now-2*(1<<bit[j]),num);
                        if(mp[i+1][j])insert(now+2*(1<<bit[j-1])-2*(1<<bit[j]),num);
                    }
                }
                else if(is_down==1&&is_right==1)
                    insert(now-(1<<bit[j-1])-(1<<bit[j]),num);
            }
        }
    }
    for(int k=1;k<=tot[cur];k++)cntt+=ans[cur][k];
    return;
}
int main(){
    getbit();
    scanf("%d%d",&n,&m);
    if(n<m){
        swap(n,m);
        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
                char ch=getchar();
                while(ch!=‘*‘&&ch!=‘_‘)ch=getchar();
                if(ch==‘_‘)mp[j][i]=1;
            }
        }
    }else{
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                char ch=getchar();
                while(ch!=‘*‘&&ch!=‘_‘)ch=getchar();
                if(ch==‘_‘)mp[i][j]=1;
            }
        }
    }
    plugdp();
    printf("%lld\n",cntt);
    return 0;
}

原文地址:https://www.cnblogs.com/luyouqi233/p/8261279.html

时间: 2024-10-16 14:12:00

BZOJ2331:[SCOI2011]地板——题解的相关文章

bzoj2331 : [SCOI2011]地板 2011-12-20

2331: [SCOI2011]地板Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 128  Solved: 54[Submit][Status][Discuss]Description lxhgww的小名叫“小L”,这是因为他总是很喜欢L型的东西.小L家的客厅是一个的矩形,现在他想用L型的地板来铺满整个客厅,客厅里有些位置有柱子,不能铺地板.现在小L想知道,用L型的地板铺满整个客厅有多少种不同的方案? 需要注意的是,如下图所示,L型地板的两端长度可

【BZOJ2331】[SCOI2011]地板 插头DP

[BZOJ2331][SCOI2011]地板 Description lxhgww的小名叫“小L”,这是因为他总是很喜欢L型的东西.小L家的客厅是一个的矩形,现在他想用L型的地板来铺满整个客厅,客厅里有些位置有柱子,不能铺地板.现在小L想知道,用L型的地板铺满整个客厅有多少种不同的方案? 需要注意的是,如下图所示,L型地板的两端长度可以任意变化,但不能长度为0.铺设完成后,客厅里面所有没有柱子的地方都必须铺上地板,但同一个地方不能被铺多次. Input 输入的第一行包含两个整数,R和C,表示客厅

【BZOJ 2331】 [SCOI2011]地板

2331: [SCOI2011]地板 Time Limit: 5 Sec  Memory Limit: 128 MB Submit: 598  Solved: 264 [Submit][Status][Discuss] Description lxhgww的小名叫"小L",这是因为他总是很喜欢L型的东西.小L家的客厅是一个的矩形,现在他想用L型的地板来铺满整个客厅,客厅里有些位置有柱子,不能铺地板.现在小L想知道,用L型的地板铺满整个客厅有多少种不同的方案? 需要注意的是,如下图所示,

【BZOJ】2331: [SCOI2011]地板 插头DP

[题意]给定n*m的地板,有一些障碍格,要求用L型的方块不重不漏填满的方案数.L型方块是从一个方格向任意两个相邻方向延伸的方块,不能不延伸.n*m<=100. [算法]插头DP [题解]状态0表示无插头,1表示能拐弯的插头,2表示不能拐弯的插头.(有插头,方块就必须必须延伸到该格),考虑转移即可. 注意可以凭空产生一个能拐弯的插头. n*m<=100,当m>10的时候将i,j互换. #include<cstdio> #include<cstring> #inclu

bzoj2330 [SCOI2011]糖果题解

http://www.lydsy.com/JudgeOnline/problem.php?id=2330 题目大意 对这个题我真的不想再多提一句了. n个人分糖,保证每个人都有糖,有k个限制条件,分别是a=b,a<b,a≥b,a>b,a≤b.这五种情况分别用x=1,2,3,4,5表示.求最少需要准备多少糖果.其中n,k≤106. 题解 一看就是差分约束了. 差分约束中求最小值用≥,跑最长路:求最大值用≤,跑最短路. x=1即a=b,直接a→b,b→a权值都是0: x=3即a≥b,直接b→a,权

2331: [SCOI2011]地板 插头DP

国际惯例的题面:十分显然的插头DP.由于R*C<=100,所以min(R,C)<=10,然后就可以愉悦地状压啦.我们用三进制状压,0表示没有插头,1表示有一个必须延伸至少一格且拐弯的插头,2表示有一个必须延伸一格且不可以拐弯的插头.转移的话就十分显然了.00->22,表示用这个格子作为开始的拐角.00->10,表示用这个格子向下延伸.00->01,表示用这个格子向右延伸.01->10,表示这个格子连接上下.01->02,表示在这个格子作为中间的拐角.02->

P3272 [SCOI2011]地板(插头DP)

[题面链接] https://www.luogu.org/problemnew/show/P3272 [题目描述] 有一个矩阵,有些点必须放,有些点不能放,用一些L型的图形放满,求方案数 [题解] (版权所有) https://www.luogu.org/blog/dedicatus545/solution-p3272 第一种情况:当前状态下,当前格子上方和左方都没有插头 这时我们需要找一个L形来把这个格子填上,那么我们可能有三种决策: 决策一:给这个格子加一个二号下插头和一个二号右插头,此时这

差分约束详解&amp;&amp;锣鼓SCOI2011糖果题解

差分约束系统: 如果一个系统由n个变量和m个约束条件组成,形成m个形如ai-aj≤k的不等式(i,j∈[1,n],k为常数),则称其为差分约束系统(system of difference constraints).亦即,差分约束系统是求解关于一组变量的特殊不等式组的方法. ——度娘. 然而并没有看懂.. 通俗来说,满足差分约束的条件是题目中给了你多个ai-aj<=(>=,<,>之类)的条件,要求同时满足这些条件并求极值的问题. 内么,怎么同时满足这些问题呢? 假如我们以这个东西为

bzoj 2331: [SCOI2011]地板 插头dp

用四进制表示状态. 用hash表把一个四进制数映射到一个小数上. 这样就可以memset了. 转移的时候分类讨论一下,特判下边界情况. 1 #include<iostream> 2 #include<cstring> 3 #include<algorithm> 4 #include<cstdio> 5 #define bq 1<<bit[j] 6 #define bp 1<<bit[j-1] 7 using namespace std