Luogu2435 染色【状压qwq】【轮廓线DP】

LINK


题目大意

有一个 n 行 m 列的格点图,你需要给每个点上染上 k 种颜色中的一种,要求没有两个相邻点颜色相同。给定第一行与最后一行的染色,试求总染色方案数。

思路

暴力预处理状态暴力转移可以得到80分的高分

这个时候司来了一句:

不要按行转移,按块转移就A了

于是改改改写了一个轮廓线

就把轮廓线上的颜色信息记录下来就好了,随便转移一下


注意终止状态也需要考虑轮廓线上的情况

需要把最后一个位置的轮廓加上


//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 Mod = 376544743;
const int N = 1e2 + 10;
const int M = 10;
const int K = 1 << (M << 1);
int n, m, k;
int s[(int)1e5 + 10][2];

int add(int a, int b) {
  return (a += b) >= Mod ? a - Mod : a;
}

int mul(int a, int b) {
  return 1ll * a * b % Mod;
}

namespace Solve1 {
void solve() {
  fu(i, 1, m) Read(s[i][0]);
  fu(i, 1, m) Read(s[i][1]);
  fu(i, 2, m) {
    if (s[i][0] == s[i - 1][0] || s[i][1] == s[i - 1][1]) {
      printf("0");
      return;
    }
  }
  fu(i, 1, m) {
    if (s[i][0] != s[i][1]) {
      if (n & 1) {
        printf("0");
        return;
      }
    } else {
      if (!(n & 1)) {
        printf("0");
        return;
      }
    }
  }
  printf("1");
}
}

namespace Solve2 {
vector<int> group;
int dp[N][K];
bool trans[8800][8800];
void dfs(int tmp, int s) {
  if (!tmp) {
    group.push_back(s);
    return;
  }
  fu(i, 0, k - 1)
    if ((s & 3) != i) dfs(tmp - 1, (s << 2) | i);
}
bool check(int s1, int s2) {
  fu(i, 1, m) {
    if (!((s1 & 3) ^ (s2 & 3))) return 0;
    s1 >>= 2;
    s2 >>= 2;
  }
  return 1;
}
void solve() {
  fu(i, 1, m) Read(s[i][0]);
  fu(i, 1, m) Read(s[i][1]);
  int bg = 0, ed = 0;
  fu(i, 2, m) {
    if (s[i][0] == s[i - 1][0] || s[i][1] == s[i - 1][1]) {
      printf("0");
      return;
    }
  }
  fu(i, 1, m) {
    bg = (bg << 2) + s[i][0];
    ed = (ed << 2) + s[i][1];
  }
  fu(i, 0, k - 1) dfs(m - 1, i);
  fv(i, group)
    fu(j, 0, i - 1)
      if (check(group[i], group[j]))
        trans[i][j] = trans[j][i] = 1;
  int posbg = 0, posed = 0;
  fv(i, group) {
    if (group[i] == bg) posbg = i;
    if (group[i] == ed) posed = i;
  }
  dp[1][posbg] = 1;
  fu(i, 2, (n >> 1)) {
    fv(j, group) if (dp[i - 1][j]) {
      fv(k, group) if (trans[j][k]) {
        dp[i][k] = add(dp[i][k], dp[i - 1][j]);
      }
    }
  }
  dp[n][posed] = 1;
  fd(i, n - 1, ((n >> 1) + 1)) {    
    fv(j, group) if (dp[i + 1][j]) {
      fv(k, group) if (trans[j][k]) {
        dp[i][k] = add(dp[i][k], dp[i + 1][j]);
      }
    }
  }
  int ans = 0;
  fv(i, group) if (dp[n >> 1][i])
    fv(j, group) if (trans[i][j])
      ans = add(ans, mul(dp[n >> 1][i], dp[(n >> 1) + 1][j]));
  Write(ans);
}
}

namespace Solve3 {
int dp[2][M][K];
void solve() {
  fu(i, 1, m) Read(s[i][0]);
  fu(i, 1, m) Read(s[i][1]);
  int bg = 0, ed = 0, up = (1 << ((m + 1) << 1)) - 1;
  fu(i, 2, m) {
    if (s[i][0] == s[i - 1][0] || s[i][1] == s[i - 1][1]) {
      printf("0");
      return;
    }
  }
  fd(i, m, 1) {
    bg = (bg << 2) + s[i][0];
    ed = (ed << 2) + s[i][1];
  }
  int ind = 0;
  dp[ind][m][bg] = 1;
  fu(i, 2, n) {
    ind ^= 1;
    memset(dp[ind], 0, sizeof(dp[ind]));
    fu(s, 0, up) if (dp[ind ^ 1][m][s])
      dp[ind][0][(s << 2) & up] = add(dp[ind][0][(s << 2) & up], dp[ind ^ 1][m][s]);
    fu(j, 1, m) {
      fu(s, 0, up) if (dp[ind][j - 1][s]) {
        fu(p, 0, k - 1) if ((((j == 1) || (((s >> ((j - 1) << 1))) & 3) != p) && ((s >> (j << 1)) & 3) != p)) {
          int nxt = (s & (up ^ (15 << ((j - 1) << 1)))) | ((p | (p << 2)) << ((j - 1) << 1));
          dp[ind][j][nxt] = add(dp[ind][j][nxt], dp[ind][j - 1][s]);
        }
      }
    }
  }
  Write(dp[ind][m][ed | (s[m][1] << (m << 1))]); // 结束状态不是ed 需要考虑最后的轮廓线状态
}
}

int main() {
#ifdef dream_maker
  freopen("input.txt", "r", stdin);
  freopen("output.txt", "w", stdout);
#endif
  Read(n), Read(m), Read(k);
  if (k == 2) Solve1::solve();
  else if (m <= 0) Solve2::solve();
  else Solve3::solve();
  return 0;
}
原文地址:https://www.cnblogs.com/dream-maker-yk/p/9907473.html