题解:P17524 [ECUSTPC 2026 Fall] 净化行动 3

· · 题解

每次拿取炮的时候一定是从中间开始拿,再那两边的,所以是拓扑排序。

如果某一行或某一列的炮 \ge4,那么这一行(列)一定是不能够拿完的,也就是有环。

如果某一行或某一列的炮 \le2,那么这一行(列)的炮不存在相互依赖的关系,不需要考虑拿的顺序,只需要挨个拿完。

如果某一行或某一列的炮 =3,那么这一行(列)拿炮的顺序一定是:先拿炮架,再拿左右(上下)两边的,所以图中的顶点是中间点指向左(上)边和右(下)边的点。

最后做一遍拓扑排序。

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
int T,n,in[N];
map<int,vector<int> > x,y;
pair<int,int> p[N];
vector<int> v[N];
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    cin >> T;
    while (T--){
        cin >> n;
        x.clear(); y.clear();
        for (int i = 1;i <= n;i++){
            v[i].clear();
            in[i] = 0;
        }
        for (int i = 1;i <= n;i++){
            cin >> p[i].first >> p[i].second;
            x[p[i].first].push_back(i);//列
            y[p[i].second].push_back(i);//行
        }
        bool flag = 0;
        for (auto i : x){
            if (i.second.size()>3){//存在环
                cout <<"NO\n";
                flag = 1;
                break;
            }
            if (i.second.size() > 2){
                int q1 = p[i.second[0]].second;
                int q2 = p[i.second[1]].second;
                int q3 = p[i.second[2]].second;
                int zuo = min({q1,q2,q3});
                int you = max({q1,q2,q3});
                if (q1 > zuo && q1 < you){//如果q1是炮架
                    v[i.second[0]].push_back({i.second[1]});
                    v[i.second[0]].push_back({i.second[2]});
                }
                if (q2 > zuo && q2 < you){//如果q2是炮架
                    v[i.second[1]].push_back({i.second[0]});
                    v[i.second[1]].push_back({i.second[2]});
                }
                if (q3 > zuo && q3 < you){//如果p3是炮架
                    v[i.second[2]].push_back({i.second[0]});
                    v[i.second[2]].push_back({i.second[1]});
                }
            }
        }
        if (flag) continue;
        for (auto i : y){
            if (i.second.size()>3){ 
                cout <<"NO\n";
                flag = 1;
                break;
            }
            if (i.second.size() > 2){
                int q1 = p[i.second[0]].first;
                int q2 = p[i.second[1]].first;
                int q3 = p[i.second[2]].first;
                int zuo = min({q1,q2,q3});
                int you = max({q1,q2,q3});
                if (q1 > zuo && q1 < you){
                    v[i.second[0]].push_back({i.second[1]});
                    v[i.second[0]].push_back({i.second[2]});
                }
                if (q2 > zuo && q2 < you){
                    v[i.second[1]].push_back({i.second[0]});
                    v[i.second[1]].push_back({i.second[2]});
                }
                if (q3 > zuo && q3 < you){
                    v[i.second[2]].push_back({i.second[0]});
                    v[i.second[2]].push_back({i.second[1]});
                }
            }
        }
        if (flag) continue;
        for (int i = 1;i <= n;i++){//统计入度
            for (auto j : v[i]){
                in[j]++;
            }
        }
        queue<int> q;
        for (int i = 1;i <= n;i++){
            if (in[i] == 0){
                q.push(i);
            }
        }
        int qwq = 0;
        while (!q.empty()){
            int u = q.front();
            q.pop();
            qwq++;
            for (auto i : v[u]){
                in[i]--;
                if (in[i] == 0){
                    q.push(i);
                }
            }
        }
        if (qwq != n){
            cout <<"NO\n";
            flag = 1;
        }
        if (flag) continue;
        cout << "YES\n";
    }
    return 0;
}