• 题解 P6378 【[PA2010]Riddle】


    题目链接

    Solution [PA2010]Riddle

    题目大意:(n) 个点 (m) 条边的无向图被分成 (k) 个部分。每个部分包含一些点。请选择一些关键点,使得每个部分恰有一个关键点,且每条边至少有一个端点是关键点。

    2-SAT、建图优化


    分析:

    首先原问题我们转化为若干个形如

    • (u,v)两点至少选一个
    • 一个部分的点集(S)内恰有一个点被选

    这样的限制条件

    这个是可以用2-SAT来判定的

    问题在于,暴力连边的话,第二个限制条件的边会非常多,但是我们注意到,对每个部分的点钦定一个顺序排成序列的话,每个点连出去的边终点是两段区间

    你大可以线段树优化建图然后尝试MLE/TLE

    前(后)缀和优化建图可以将边数优化到(O(n))级别,然后就可以放心的跑2-SAT了

    #include <cstdio>
    #include <cctype>
    #include <vector>
    #include <stack>
    using namespace std;
    const int maxn = 5e6 + 100;
    inline int read(){
        int x = 0;char c = getchar();
        while(!isdigit(c))c = getchar();
        while(isdigit(c))x = x * 10 + c - '0',c = getchar();
        return x;
    }
    struct edge{int u,v;};
    vector<edge> edges;
    vector<int> G[maxn],vec[maxn];
    inline void addedge(int u,int v){
        G[u].push_back(v);}
    int n,m,k,tot,dfs_tot,scc_tot,scc[maxn],to[maxn],dfn[maxn],low[maxn],instk[maxn];
    stack<int> stk;
    inline void tarjan(int u){
        dfn[u] = low[u] = ++dfs_tot;
        stk.push(u),instk[u] = 1;
        for(int v : G[u]){
            if(!dfn[v])tarjan(v),low[u] = min(low[u],low[v]);
            else if(instk[v])low[u] = min(low[u],dfn[v]);
        }
        if(low[u] == dfn[u]){
            scc_tot++;
            int t;
            do{
                t = stk.top();stk.pop(),instk[t] = 0;
                scc[t] = scc_tot;
            }while(t != u);
        }
    }
    int main(){
        n = read(),m = read(),k = read();
        for(int u,v,i = 1;i <= m;i++)
            u = read(),v = read(),edges.push_back(edge{u,v});
        for(int x,i = 1;i <= k;i++){
            int num = read();
            while(num--)x = read(),vec[i].push_back(x),to[x] = ++tot;
        }
        for(int i = 1;i <= n;i++)addedge(n + i,i),addedge(2 * n + i,i);
        for(int id = 1;id <= k;id++){
            for(unsigned int i = 0;i < vec[id].size();i++){
                if(i != 0)addedge(n + to[vec[id][i]],n + to[vec[id][i]] - 1),addedge(3 * n + vec[id][i],n + to[vec[id][i]] - 1);
                if(i != vec[id].size() - 1)addedge(2 * n + to[vec[id][i]],2 * n + to[vec[id][i]] + 1),addedge(3 * n + vec[id][i],2 * n + to[vec[id][i]] + 1);
            }
        }
        for(auto e : edges){
            addedge(to[e.u],3 * n + e.v);
            addedge(to[e.v],3 * n + e.u);
        }
        for(int i = 1;i <= 4 * n;i++)
            if(!dfn[i])tarjan(i);
        for(int i = 1;i <= n;i++)
            if(scc[to[i]] == scc[3 * n + i])return puts("NIE"),0;
        puts("TAK");
        return 0;
    }
    
  • 相关阅读:
    作业3
    数组求和
    2.自己的Github试用过程
    2.自己的Github注册流程
    图片左右滑动整理为插件
    artDialog中的time参数,ajax请求中的异步与同步
    简单的图片放大镜效果插件
    3月份学习安排
    前端好的网站
    web app开发中遇到的问题
  • 原文地址:https://www.cnblogs.com/colazcy/p/13543299.html
Copyright © 2020-2023  润新知