1. 程式人生 > >luoguP3943 星空 最短路 一般圖的最大匹配 狀壓Dp

luoguP3943 星空 最短路 一般圖的最大匹配 狀壓Dp

luoguP3943 星空

題目傳送門

分析

區間操作很難搞,一個巧妙的轉化是,把原區間差分,這樣的話異或操作就變成了將某兩個位置取反。
原問題轉化為某個01序列上有若干個1,每次可以把距離為 b i + 1 b_i+1

的兩個1取反,求把這個序列清空的最少操作次數。
考慮如果只有兩個 1 1 ,實際上就是跑一個最短路。
如果求出了每個 1 1 兩兩之間的距離,這樣的話就轉化成了一個一般圖上的最大匹配問題。
上帶花樹啊

開始最多 8 8 個點,考慮狀壓,每次列舉最左的未匹配的節點去和別的節點匹配轉移即可。

程式碼

#include<cstdio>
#include<cstring>
const int N = 4e4 + 10, M = 110, X = 1048576;
int ri() {
    char c = getchar(); int x = 0, f = 1; for(;c < '0'
|| c > '9'; c = getchar()) if(c == '-') f = -1; for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f; } int d[M][N], c[M], a[M], q[N], f[X], bin[21], Lg[X], n, m, k, C; bool b[N]; void Up(int &a, int b) {b < a || !~a ? a = b : 0;} void Bfs(int *d, int s) { int L = 1, R = 1; q[L] = s; d[s] = 1; for(int u = q[L]; L <= R; u = q[++L]) for(int i = 1;i <= m; ++i) { if(u + c[i] <= n && !d[u + c[i]]) d[q[++R] = u + c[i]] = d[u] + 1; if(u - c[i] >= 0 && !d[u - c[i]]) d[q[++R] = u - c[i]] = d[u] + 1; } } int main() { n = ri(); k = ri(); m = ri(); for(int i = 1, x;i <= k; ++i) x = ri(), b[x] ^= 1, b[x - 1] ^= 1; for(int i = 1;i <= m; ++i) c[i] = ri(); for(int i = 0;i <= n; ++i) if(b[i]) a[C++] = i; for(int i = 0;i < C; ++i) Bfs(d[i], a[i]); std::memset(f, -1, sizeof(f)); f[0] = 0; bin[0] = 1; for(int i = 1;i <= C; ++i) bin[i] = bin[i - 1] << 1, Lg[bin[i]] = Lg[bin[i - 1]] + 1; for(int i = 0;i < bin[C] - 1; ++i) if(~f[i]) { int j = Lg[(i + 1)&-(i + 1)]; for(int k = j + 1;k < C; ++k) if(~i & bin[k]) if(d[j][a[k]]) Up(f[i | bin[j] | bin[k]], f[i] + d[j][a[k]] - 1); } printf("%d\n", f[bin[C] - 1]); return 0; }