题目大意:有一个由小灯泡和电线连成的DAG(有向无环图),其中一号小灯泡的灯是亮着的,并且不会有边指向一号灯,每个灯泡上有一个开关,拨动开关会沿着电线一路改变灯泡的状态,即亮着的灯会熄灭,熄灭的灯则会亮起。状态改变只能顺着有向弧的方向传播,具体来说,如果 u,v 之间有一个有向弧 (u,v),拨动 u 的开关时,u 和 v 的状态都会改变,但拨动 v 的开关时,u 的状态不会改变。
如果要使所有灯熄灭,至少要拨动多少次开关。
按拓扑序的顺序拨动开关,后面拨动的开关不会影响前面的灯泡,显然对于DAG来说一定有解,关键在于如何判断当前这个灯泡是否需要拨动开关,需要求出前面拨动的灯泡有哪些,如果前面有偶数个灯泡拨动,那么这个灯泡一定是暗的,反之需要拨动这个灯泡。
普通的动态规划无法合并得到影响这个灯泡的数量或集合,观察到数据量只有 40000,对每个点维护一个 bitset,使用位运算来优化 dp 来合并得到影响这个灯泡的灯泡集合。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 4e4 + 10;
int d[maxn],s[maxn];
vector<int> g[maxn];
bitset<maxn> bit[maxn];
int n,m,ans;
void tpsort() {
queue<int> q;
q.push(1);
bit[1].set(1);
while (!q.empty()) {
int u = q.front(); q.pop();
if (bit[u].count() & 1)
ans++, s[u] = 1;
for (auto it : g[u]) {
d[it]--;
bit[it] |= bit[u];
if (s[u]) bit[it].set(u);
if (d[it] == 0)
q.push(it);
}
}
}
int main() {
scanf("%d%d",&n,&m);
for (int i = 1; i <= m; i++) {
int u, v; scanf("%d%d",&u,&v);
g[u].push_back(v);
d[v]++;
}
tpsort();
printf("%d\n",ans);
return 0;
}