luoguP3943 星空 最短路 一般图的最大匹配 状压Dp

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lvzelong2014/article/details/83420007

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;
}

猜你喜欢

转载自blog.csdn.net/lvzelong2014/article/details/83420007