1 条题解

  • 0
    @ 2026-5-6 1:22:40

    【形式化题意】

    A 和 B 在一张有向图上进行博弈,qq 次询问指定两个点放置两个棋子,每轮 A 可以指定一个棋子,B 可以选择一条边移动这枚棋子;如果游戏会在有限次内结束,则 A 胜,否则 B 胜。

    【解法】

    如果存在一个棋子位于出度为 00 的点,则 A 可以指定这个节点,即为 A 必胜点。

    如果棋子位于的节点的所有出边都指向 A 必胜点,则这个节点也为 A 必胜点。

    形式化地,在反图上跑拓扑排序,反图上拓扑过程中入度为零的点都是 A 必胜点。

    先做一次拓扑处理掉这些点,则剩下的点都至少可以到达一个环。

    理论上来说,这时候可以随便走了,但是注意到两个棋子在任何时刻不允许处于同一个结点上,所以可以通过一个棋子阻挡另一个的方式,使 A 获胜。

    当一个点出度 2\geq2 时,一定不可以挡住。则考虑出度为 11 的点,或者说考虑一条出度均为 11 的链。

    如果两个棋子都在这条链上,A 反复指定“靠上”的那个棋子,B 没有其他选择,最终与另一个棋子撞上,使 A 获胜。

    而我们注意到,在这条出度为 11 的链上,两个棋子具体放在哪里无所谓,所以我们可以把链上的点即所有 11 度点全部缩在一起,用并查集维护。

    此时如果两个点中存在一个点被第一次拓扑删除,或者在缩完之后缩到了一个点,是 A 胜,否则是 B 胜。

    用 set 维护出边,利用启发式合并思想合并两个节点,可以使复杂度有保障。

    【code】

    #include<bits/stdc++.h>
    using namespace std;
    
    const int nr = 1e5 + 10;
    int n, m, ft[nr];
    set<int> g[nr], ex[nr];
    bool del[nr];
    
    int find(int x)
    {
    	if (ft[x] == x) return x;
    	return ft[x] = find(ft[x]);
    }
    
    signed main()
    {
    	ios::sync_with_stdio(0);
    	cin.tie(0), cout.tie(0);
    	cin >> n >> m;
    	for (int i = 1; i <= n; i++) ft[i] = i;
    	for (int i = 1; i <= m; i++)
    	{
    		int u, v;
    		cin >> u >> v;
    		g[u].insert(v), ex[v].insert(u);
    	}
    	queue<int> q;
    	for (int i = 1; i <= n; i++) if (!g[i].size()) q.push(i);
    	while (!q.empty())
    	{
    		int u = q.front();
    		q.pop();
    		del[u] = true;
    		for (set<int>::iterator it = ex[u].begin(); it != ex[u].end(); it++)
    		{
    			g[*it].erase(u);
    			if (!g[*it].size()) q.push(*it);
    		}
    		ex[u].clear();
    	}
    	for (int i = 1; i <= n; i++)
    		if (!del[i] && g[i].size() == 1) q.push(i);
    	while (!q.empty())
    	{
    		int u = q.front();
    		q.pop();
    		u = find(u);
    		int v = *g[u].begin();
    		if (u == v || g[u].size() != 1) continue; 
    		g[u].erase(v), ex[v].erase(u);
    		if (g[v].size() + ex[v].size() > g[u].size() + ex[u].size()) swap(u, v);
    		ft[v] = u;
    		bool cir = false;
    		for (set<int>::iterator it = g[v].begin(); it != g[v].end(); it++)
    		{
    			if (*it == v) { cir = true; continue; }
    			ex[*it].erase(v), ex[*it].insert(u);
    			g[u].insert(*it);
    		}
    		for (set<int>::iterator it = ex[v].begin(); it != ex[v].end(); it++)
    		{
    			if (*it == v) { cir = true; continue; } 
    			g[*it].erase(v), g[*it].insert(u);
    			ex[u].insert(*it);
    			if (g[*it].size() == 1) q.push(*it);
    		}
    		if (cir) g[u].insert(u), ex[u].insert(u);
    		g[v].clear(), ex[v].clear();
    		if (g[u].size() == 1) q.push(u); 
    	}
    	int Q;
    	cin >> Q;
    	while (Q--)
    	{
    		int x, y;
    		cin >> x >> y;
    		cout << (del[find(x)] || del[find(y)] || find(x) == find(y) ? 'B' : 'H');
    	}
    	cout << '\n';
    	return 0;
    }
    
    
    • 1

    信息

    ID
    7649
    时间
    4000ms
    内存
    512MiB
    难度
    9
    标签
    递交数
    10
    已通过
    5
    上传者