题解:P14334 [JOI2021 预选赛 R2] 间谍 2 / Spy 2

这边是题目传送门喵!

题意简述

题面是有一定的迷惑性的。不和一般的情况一样间谍说的都是假话,非间谍说的都是真话,实际上非间谍给的情报根本不起作用,因为其真假性无法判断(这么看来确实是间谍在提供情报呢)。由于间谍说的话可以判断,那么在判断了间谍所说的某一句话是真话的情况下,可以推出他说的另一句话必然是假话。

思路

正因上一段提到过的性质,将未知身份的人默认为非间谍显然可以条件使得更容易成立。

因此不难想到对每一个间谍罗列出所有与他相关的信息。通过他作为间谍的特性推出更多结论。因此整个过程就类似于 BFS 的过程,用队列存储间谍的编号,讨论完一个间谍再讨论下一个。讨论他的时候推出矛盾就说明存在问题;讨论他的时候推出了新的间谍则将新的间谍也加入间谍队列中。

然后对于每一个间谍的讨论就是特别细致的分类讨论即可。具体代码实现还是比较好写的。

代码

看起来较长是因为注释比较多。分类讨论时的具体代码实现都写在注释里面了。

#include<bits/stdc++.h>
#define ll long long
#define ios ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int N=300005;
int n,m,a[N],b[N],c[N];
int t[N];
vector<int> g[N];  // g[i]: 存储与议员i相关的所有信息编号
queue<int> q;

int main() {
    ios;
    cin>>n>>m;
    for(int i=1;i<=n;i++) {
        cin>>t[i];
        if(t[i]==1)q.push(i);  // 确定的间谍
    }
    for(int i=1;i<=m;i++) {
        cin>>a[i]>>b[i]>>c[i];
        // 将信息编号与涉及的三个议员关联
        g[a[i]].push_back(i);
        g[b[i]].push_back(i);
        g[c[i]].push_back(i);
    }
    
    // BFS传播约束
    while(!q.empty()) {
        int u=q.front();q.pop();  // 当前处理的议员
        for(auto i:g[u]) {
            if(u==a[i]&&t[u]==1) {  // Case 1:当前议员是发言人 A 且是间谍
                // 此时 B 是非间谍或 C 是间谍
                if(t[b[i]]==1&&t[c[i]]==2) { // 矛盾情况
                    cout<<"-1\n";
                    return 0;
                }
                // 如果确定 B 是间谍,那么 C 必须也是间谍
                if(t[b[i]]==1&&t[c[i]]==3) {
                    t[c[i]]=1;
                    q.push(c[i]);
                }
                // 如果确定 C 是非间谍,那么 B 必须也是非间谍
                else if(t[c[i]]==2&&t[b[i]]==3) {
                    t[b[i]]=2;
                }
            }
            // Case 2:当前议员是 B 且是间谍
            else if(u==b[i]&&t[u]==1) {
                
                if(t[a[i]]==1) {  // 如果发言人 A 是间谍,那么他的陈述必须为假
                    // C 必须是间谍
                    if(t[c[i]]==2) {  // 矛盾
                        cout<<"-1\n";
                        return 0;
                    }
                    if(t[c[i]]==3) {  // C未知,则确定其为间谍
                        t[c[i]]=1;
                        q.push(c[i]);
                    }
                }
            }
            // Case 3:当前议员是 C 且不是间谍
            else if(u==c[i]&&t[u]==2) {
                // 如果发言人A是间谍,那么他的陈述必须为假
                if(t[a[i]]==1) {
                    // B 必须是非间谍
                    if(t[b[i]]==1) {  // 矛盾
                        cout<<"-1\n";
                        return 0;
                    }
                    if(t[b[i]]==3) {  // B 未知,则确定其为非间谍
                        t[b[i]]=2;
                    }
                }
            }
        }
    }
    // 再复查一遍(可删去)
    for(int i=1;i<=m;i++) {
        if(t[a[i]]==1) {  // 若发言人是间谍,则陈述必须为假:B 不是间谍或 C 是间谍
            if(t[b[i]]==1&&t[c[i]]==2) {  // 矛盾:B 是间谍且C 不是间谍
                cout<<"-1\n";
                return 0;
            }
        }
    }
    // 未知的统一设为不是间谍
    for(int i=1;i<=n;i++) {
        // 如果是 3 就直接输出 2 即可
        cout<<min(t[i],2)<<'\n';
    }
    return 0;
}

完结撒花!

posted @ 2025-11-14 13:05  Circle_Table  阅读(1)  评论(0)    收藏  举报