吉林大学ACM集训队选拔赛(重现赛)C:Strange Bulbs(拓扑排序 + bitset优化)

在这里插入图片描述


题目大意:有一个由小灯泡和电线连成的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;
}

猜你喜欢

转载自blog.csdn.net/qq_41997978/article/details/107395950