• 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
    ",cntt);
        return 0;
    }
  • 相关阅读:
    电子邮件的工作原理
    常用邮箱服务器地址端口
    wpf \silverlight 保存控件为图片
    GIS理论(墨卡托投影、地理坐标系、地面分辨率、地图比例尺、Bing Maps Tile System)【转载】
    Visifire图表控件官网地址
    Ado方式导入excel混用数据类型引起数据缺失问题解决方法
    c#日期时间的操作
    获得excel的sheet名字
    正则表达式验证可发短信的号码,如手机号和小灵通号码(106+区号+号码)
    验证多行文本框输入长度的正则表达式
  • 原文地址:https://www.cnblogs.com/luyouqi233/p/8261279.html
Copyright © 2020-2023  润新知