第九届河南省赛 宣传墙 //状压dp+矩阵快速幂+dfs

http://nyoj.top/problem/1273

状压dp+矩阵快速幂+dfs

 

 

1273-宣传墙

 
  • 内存限制:64MB 时间限制:1000ms 特判: No

  • 通过数:19 提交数:64 难度:4

题目描述:

ALPHA 小镇风景美丽,道路整齐,干净,到此旅游的游客特别多。CBA 镇长准备在一条道路南 面 4*N 的墙上做一系列的宣传。为了统一规划,CBA 镇长要求每个宣传栏只能占相邻的两个方格 位置。但这条道路被另一条道路分割成左右两段。CBA 镇长想知道,若每个位置都贴上宣传栏, 左右两段各有有多少种不同的张贴方案。 例如: N=6,M=3, K=2, 左,右边各有 5 种不同的张贴方案 


 

输入描述:

第一行: T 表示以下有 T 组测试数据 ( 1≤T ≤8 )
接下来有T行, 每行三个正整数 N M K 分别表示道路的长度,另一条道路的起点和宽度
(1≤ N ,M ≤ 1 000 000, 1≤ K ≤ 100000)

输出描述:

每组测试数据,输出占一行:两个整数,分别表示左右两段不同的张贴方案数。由于方案总数
可能很大,请输出对 997 取模后的结果。

样例输入:

复制

2
6 3 2
5 3 2

样例输出:

5 5
5 1

 一开始按poj铺砖块的裸题写),直接与处理1e6的答案,tle了。

然后把1e6的数据粘在数组里交,告诉我代码太长。。。

然后发现只有4行,那么直接把所有的可能转移的状态都写出来,题目放过了这种做法。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int mod = 997;
const int max_n= 1e6+5;
int dp[max_n][1<<4];
int main(){
    dp[0][0]=1;
    for(int i=0;i<=1e6+1;i++){
        dp[i+1][0]=(dp[i+1][0]+dp[i][0]+dp[i][3]+dp[i][9]+dp[i][12]+dp[i][15])%mod;
        dp[i+1][3]=(dp[i][0]+dp[i][12]+dp[i+1][3])%mod;
        dp[i+1][9]=(dp[i][0]+dp[i][6]+dp[i+1][9])%mod;
        dp[i+1][12]=(dp[i+1][12]+dp[i][0]+dp[i][3])%mod;
        dp[i+1][15]=(dp[i][0]+dp[i+1][15])%mod;
        dp[i+1][6]=(dp[i+1][6]+dp[i][9])%mod;
    }
    int T;cin>>T;
    while(T--){
       int n,m,k;cin>>n>>m>>k;
       int a=m-1;
       int b=n-a-k;
       cout<<dp[a][0]<<' '<<dp[b][0]<<endl;
    }
    return 0;
}

如果不是4行,而是x行,如果n到达1e10,只有用矩阵快速幂+dfs+状压dp了。

通过上面发现f(n)的每一个状态都是由f(n-1)的每一个状态转移而来,那么就直接上矩阵快速幂

用dfs构造转移的矩阵。

 

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int mod = 997;
const int max_n= 1e6+5;
LL nn;
struct mat{
    LL m[17][17];
};
mat unit,init; //单位矩阵
mat matmul(mat a,mat b){
    mat re;
    memset(re.m,0,sizeof(re.m));
    for(int i=0;i<nn;i++){
        for(int j=0;j<nn;j++){
            for(int k=0;k<nn;k++){
                re.m[i][j]=(re.m[i][j]+a.m[i][k]*b.m[k][j])%mod;
            }
        }
    }
    return re;
}
mat quickmod(LL x,mat jin){
    mat re;
    memset(re.m,0,sizeof(re.m));
    for(int i=0;i<nn;i++) re.m[i][i]=1;
    while(x){
        if(x&1)
            re=matmul(re,jin);
        x=x>>1;
        jin=matmul(jin,jin);
    }
    return re;
}

void dfs(int j,int h,int st,int toth){ //前一列满,当前列二进制是j,后一列二进制是st
    if(h==toth+1) {     //递归出口
        unit.m[st][j]=1; //构造矩阵。
        return ;
    }
    if((j&(1<<(h-1)))>0) {  //当前行不能放
        dfs(j,h+1,st,toth);return;
    }
    if(h==toth){        //最后一行只能横着放
        dfs(j,h+1,st+(1<<(h-1)),toth);
        return ;
    }
    dfs(j,h+1,st+(1<<(h-1)),toth);
    if((j&(1<<(h)))==0)  //竖着放
        dfs(j,h+2,st,toth);
}
int main(){
    memset(unit.m,0,sizeof(unit.m)); //单位矩阵
    for(int i=0;i<1<<4;i++){
        dfs(i,1,0,4);
    }
    memset(init.m,0,sizeof(init.m)); //初始矩阵
    init.m[0][0]=1;
    int T;cin>>T;
    while(T--){
       int n,m,k;cin>>n>>m>>k;
       int a=m-1;
       int b=n-a-k;
       nn=1<<4;
       mat ans1=quickmod(a,unit);
       ans1=matmul(init,ans1);
       mat ans2=quickmod(b,unit);
       ans2=matmul(init,ans2);
       cout<<ans1.m[0][0]<<' '<<ans2.m[0][0]<<endl;
    }
    return 0;
}

 

 


版权声明:本文为qq_41730604原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。