BZOJ3195: [Jxoi2012]奇怪的道路【状压DP】

Description

小宇从历史书上了解到一个古老的文明。这个文明在各个方面高度发达,交通方面也不例外。考古学家已经知道,这个文明在全盛时期有n座城市,编号为1..n。m条道路连接在这些城市之间,每条道路将两个城市连接起来,使得两地的居民可以方便地来往。一对城市之间可能存在多条道路。
据史料记载,这个文明的交通网络满足两个奇怪的特征。首先,这个文明崇拜数字K,所以对于任何一条道路,设它连接的两个城市分别为u和v,则必定满足1 <=|u - v| <= K。此外,任何一个城市都与恰好偶数条道路相连(0也被认为是偶数)。不过,由于时间过于久远,具体的交通网络我们已经无法得知了。小宇很好奇这n个城市之间究竟有多少种可能的连接方法,于是她向你求助。
方法数可能很大,你只需要输出方法数模1000000007后的结果。

Input

输入共一行,为3个整数n,m,K。

Output

输出1个整数,表示方案数模1000000007后的结果。

Sample Input

【输入样例1】
3 4 1
【输入样例2】
4 3 3

Sample Output

【输出样例1】
3

【输出样例2】
4
【数据规模】

HINT

100%的数据满足1 <= n <= 30, 0 <= m <= 30, 1 <= K <= 8.

【题目说明】

两种可能的连接方法不同当且仅当存在一对城市,它们间的道路数在两种方法中不同。

在交通网络中,有可能存在两个城市无法互相到达。


思路

大暴力切题。。。

理论复杂度上线\(n^4*2^{k+1}\)

别问我为啥可以过。。。

思路就是记录下前面k个点的链接边的奇偶性

然后暴力枚举当前和哪一个点练多少条边

判断一下转移条件就可以啦

注意对当前节点链接节点的枚举要在最外层

不然会出事情

142857说可以优化掉一个n,但是我懒了

直接暴力转移暴力dp反正可以过,还比某妹神的dp快到不知道哪里去


//Author: dream_maker
#include<bits/stdc++.h>
using namespace std;
//----------------------------------------------
typedef pair<int, int> pi;
typedef long long ll;
typedef double db;
#define fi first
#define se second
#define fu(a, b, c) for (int a = b; a <= c; ++a)
#define fd(a, b, c) for (int a = b; a >= c; --a)
#define fv(a, b) for (int a = 0; a < (signed)b.size(); ++a)
const int INF_of_int = 1e9;
const ll INF_of_ll = 1e18;
template <typename T>
void Read(T &x) {
  bool w = 1;x = 0;
  char c = getchar();
  while (!isdigit(c) && c != '-') c = getchar();
  if (c == '-') w = 0, c = getchar();
  while (isdigit(c)) {
    x = (x<<1) + (x<<3) + c -'0';
    c = getchar();
  }
  if (!w) x = -x;
}
template <typename T>
void Write(T x) {
  if (x < 0) {
    putchar('-');
    x = -x;
  }
  if (x > 9) Write(x / 10);
  putchar(x % 10 + '0');
}
//----------------------------------------------
const int N = 32;
const int K = 10;
const int Mod = 1e9 + 7;
int dp[N][N][1 << K];
int n, m, k;

void add(int &a, int b) {
  if ((a += b) >= Mod) a -= Mod;
}

int main() {
#ifdef dream_maker
  freopen("input.txt", "r", stdin);
  freopen("output.txt", "w", stdout);
#endif
  Read(n), Read(m), Read(k);
  dp[1][0][0] = 1;
  int up = (1 << (k + 2)) - 1;
  fu(i, 2, n) {
    fu(j, 0, m)
      fu(l, 0, up) if ((l & 1) == 0 && dp[i - 1][j][l])
        add(dp[i][j][l >> 1], dp[i - 1][j][l]);
    fu(j, 1, min(k, i - 1))
      fd(l, m, 0)
        fu(p, 0, up)
          fu(e, 1, l) if (dp[i][l - e][p])
            add(dp[i][l][p ^ ((e & 1) << (k + 1)) ^ ((e & 1) << (k - j + 1))], dp[i][l - e][p]);
  }
  Write(dp[n][m][0]);
  return 0;
}
posted @ 2018-11-05 11:32  Dream_maker_yk  阅读(193)  评论(0编辑  收藏  举报