算法进阶-2sat-cf-1657F

欢迎关注更多精彩
关注我,学习常用算法与数据结构,一题多解,降维打击。

题目大意

https://codeforces.com/contest/1657/problem/F
给 定 一 棵 n 个 结 点 ( 每 个 结 点 上 标 记 有 一 个 小 写 字 母 ) 的 数 和 q 个 三 元 组 ( x i , y i , s i ) 。 给定一棵n个结点(每个结点上标记有一个小写字母)的数和q个三元组(x_i,y_i,s_i)。 nq(xi,yi,si)

x i , y i 是 树 上 的 2 个 结 点 编 号 , s i 是 一 个 小 写 字 母 组 成 的 字 符 串 , 长 度 是 从 树 上 x i 到 y i 的 路 径 经 过 结 点 的 个 数 x_i, y_i 是树上的2个结点编号,s_i是一个小写字母组成的字符串,\\长度是从树上x_i到y_i的路径经过结点的个数 xi,yi2sixiyi

如 果 把 从 x i 到 y i ( 可 y i 到 x i ) 路 径 的 字 母 顺 序 排 列 可 得 到 s i 则 s i 是 合 法 的 。 如果把从x_i到y_i(可y_i到x_i)路径的字母顺序排列可得到s_i则s_i是合法的。 xiyiyixisisi
问每个结点的字母要怎么选择才能满足所有si.

分析&思路

对于有被s覆盖的点来说,选择就只有2个,要么顺序摆放,要么倒序摆放,对于没有被s覆盖的点,可以直接取a。基本思路就是用2sat来解决。

分析一下2sat建立边的过程。
对于结点i来说有2个选择ci1, ci2,设置变量pi, 如果pi=false则选择ci1, 否则选择ci2。这样还不够,因为每个结点之间还要通过s来联系,所以再加入一个变量wj, 如果wj=false代表sj是从xj到yj顺序放,否则是倒序放。

首先根据路径查找出sj对应到树上的结点。然后标记这些节点的可选字母。
假设对于一个sj来说, 结点数组为A,第k个结点可以选择C1, C2.

然后是根据条件进行设置。
假 设 第 i 个 结 点 对 应 s j 路 径 上 的 第 k 个 顺 序 结 点 假设第i个结点对应s_j路径上的第k个顺序结点 isjk
1 如 果 s j , k ! = c i , 1 , 设 置 条 件 p i = t r u e ∣ ∣ w j = t r u e 1 如果s_{j,k}!=c_{i,1}, 设置条件p_i =true || w_j = true 1sj,k!=ci,1,pi=truewj=true

2 如 果 s j , k ! = c i , 2 , 设 置 条 件 p i = f a l s e ∣ ∣ w j = t r u e 2 如果s_{j,k}!=c_{i,2}, 设置条件p_i =false || w_j = true 2sj,k!=ci,2,pi=falsewj=true

3 如 果 s j , ∣ s j ∣ − k + 1 ! = c i , 1 , 设 置 条 件 p i = t r u e ∣ ∣ w j = f a l s e 3 如果s_{j,|s_j|-k+1}!=c_{i,1}, 设置条件p_i =true || w_j = false 3sj,sjk+1!=ci,1,pi=truewj=false

3 如 果 s j , ∣ s j ∣ − k + 1 ! = c i , 2 , 设 置 条 件 p i = f a l s e ∣ ∣ w j = f a l s e 3 如果s_{j,|s_j|-k+1}!=c_{i,2}, 设置条件p_i =false || w_j = false 3sj,sjk+1!=ci,2,pi=falsewj=false

AC代码


#include <iostream>
#include <stdio.h>
#include <queue>
#include <string.h>
#include <stack>
#include <vector>
#include <algorithm>
#include <assert.h>

using namespace std;


const int N = 4e6 +10;
const int E = 4e6+10;
// 边属性
class Edge {
public:
    int toVertex;
    int nextEdge;
};

// 点属性
class Node {
public:
    int head;
};

class Graph {
public:
    Edge edges[E];
    Node nodes[N];
    int usedEdge=0;
    Graph() {
        usedEdge = 0;
    }

    void initEdge(int n) {
        for(int i=0;i<=n;++i) {
            nodes[i].head=-1;
        }
        usedEdge = 0;
    }

    void addEdge(int a, int b) {
        if(a==b) return;
        edges[usedEdge].nextEdge = nodes[a].head;
        nodes[a].head = usedEdge;
        edges[usedEdge].toVertex = b;

        usedEdge++;

        // cout<<"add edge: "<<a<<" "<<b<<endl;
    }
};

Graph og, tree;

int father[N], high[N];

void initHigh(int u) {
    for(int i=tree.nodes[u].head;i>=0; i=tree.edges[i].nextEdge) {
        int v = tree.edges[i].toVertex;
        if(v==father[u])continue;
        father[v]=u;
        high[v]=high[u]+1;
        initHigh(v);
    }
}

vector<int> getPath(int u, int v) {
    vector<int> l, r;

    while(u!=v) {
        if(high[u]>high[v]) {
            l.push_back(u);
            u=father[u];
        }else {
            r.push_back(v);
            v=father[v];
        }
    }

    l.push_back(u);
    l.insert(l.end(), r.rbegin(), r.rend());

    return l;
}


int dfn[N], low[N];
stack<int> st;
int deep, sum;
int color[N];

void initTarjan() {
    deep = 0;
    sum=0;
    memset(dfn, 0,sizeof(dfn));
    memset(low, 0,sizeof(low));
    memset(color, 0,sizeof(color));
}

void tarjan(int u) {
    dfn[u] = ++deep;
    low[u] = deep;
    st.push(u);

    for(int i=og.nodes[u].head;i>=0;i = og.edges[i].nextEdge) {
        int v = og.edges[i].toVertex;
        if(!dfn[v]) {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        } else if(!color[v]) {
            low[u] = min(low[u], dfn[v]);
        }
    }

    if(dfn[u]==low[u]) {
        color[u] = ++sum;
        while(st.top()!=u) {
            int v = st.top();
            st.pop();
            color[v]=sum;
        }
        st.pop();
    }
}

pair<char, char> opts[N];

char cstr[N];

void solve() {
    int n, m;
    scanf("%d%d", &n, &m);
    for(int i=1;i<=n;++i) {
        opts[i] = {'a','a'};
    }
    int nm =  (n*2+m*2);

    tree.initEdge(n+10);
    for(int i=1;i<n;++i) {
        int a, b;
        scanf("%d%d", &a, &b);
        tree.addEdge(a, b);
        tree.addEdge( b,a);
    }
    father[1]=-1;
    high[1]=1;

    initHigh(1);
    vector<vector<int>> paths(m);
    vector<string> strs(m);
    og.initEdge(nm+10);
    for(int i=0;i<m;++i) {
        int u, v;
        scanf("%d%d%s", &u, &v, cstr);
        strs[i]=cstr;
        // cout<<u<<v<<strs[i]<<endl;
        // continue;
        paths[i] = getPath(u, v);
        /*
        for(auto x:paths[i]) {
            printf("%d ", x);
        }
        puts("");
         */
        int sl = strlen(cstr);
        assert(int(paths[i].size()) == sl);
        for(int j=0;j<sl;++j) {
            opts[paths[i][j]] = {cstr[j], cstr[sl-1-j]};
        }
    }

    for(int i=0;i<m;++i) {
        int sl = strs[i].size();
        for(int j=0;j<sl;++j) {
            int v = paths[i][j];
            int c = strs[i][j], rc = strs[i][sl-1-j];
            int d = opts[v].first, rd = opts[v].second;
            if(c!=d) {
                og.addEdge(v+n, 2*n+1+i);
                og.addEdge(2*n+1+i+m, v);
            }

            if(c!=rd) {
                og.addEdge(v, 2*n+1+i);
                og.addEdge(2*n+1+i+m, v+n);
            }

            if(rc!=d) {
                og.addEdge(v+n, 2*n+1+i+m);
                og.addEdge(2*n+1+i, v);
            }

            if(rc!=rd ) {
                og.addEdge(v, 2*n+1+i+m);
                og.addEdge(2*n+1+i, v+n);
            }
        }
    }

    initTarjan();
    for(int i=1;i<=nm;++i) {
        if(!dfn[i])tarjan(i);
    }

    for(int i=1;i<=n;++i) {
        // printf("%d %c %c\n", i, opts[i].first, opts[i].second);
        if(color[i]==color[i+n]) {
            puts("NO");
            return;
        }
    }


    for(int i=2*n+1;i<=2*n+m;++i) {
        if(color[i]==color[i+m]) {
            puts("NO");
            return;
        }
    }

    puts("YES");

    for(int i=1;i<=n;++i) {
        if(color[i]<color[i+n])putchar(opts[i].second);
        else putchar(opts[i].first);
    }
    puts("");
    /*
    for(int i=2*n+1;i<=2*n+m;++i) {
        if(color[i]<color[i+m])putchar('r');
        else putchar('o');
    }
    puts("");
     */
}


int main() {
    solve();
    return 0;
}
/*
10 10
1 2
1 3
1 4
1 5
1 6
1 7
1 8
1 9
1 10
1 2 ab
1 3 ab
1 4 ab
1 5 ab
1 6 ab
1 7 ab
1 8 ab
1 9 ab
1 10 ab
10 2 aba

4 3
1 2
1 3
1 4
1 2 ab
3 2 aba
1 4 ab
 */


本人码农,希望通过自己的分享,让大家更容易学懂计算机知识。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值