欢迎关注更多精彩
关注我,学习常用算法与数据结构,一题多解,降维打击。
https://codeforces.com/contest/1657/problem/F
给
定
一
棵
n
个
结
点
(
每
个
结
点
上
标
记
有
一
个
小
写
字
母
)
的
数
和
q
个
三
元
组
(
x
i
,
y
i
,
s
i
)
。
给定一棵n个结点(每个结点上标记有一个小写字母)的数和q个三元组(x_i,y_i,s_i)。
给定一棵n个结点(每个结点上标记有一个小写字母)的数和q个三元组(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,yi是树上的2个结点编号,si是一个小写字母组成的字符串,长度是从树上xi到yi的路径经过结点的个数
如
果
把
从
x
i
到
y
i
(
可
y
i
到
x
i
)
路
径
的
字
母
顺
序
排
列
可
得
到
s
i
则
s
i
是
合
法
的
。
如果把从x_i到y_i(可y_i到x_i)路径的字母顺序排列可得到s_i则s_i是合法的。
如果把从xi到yi(可yi到xi)路径的字母顺序排列可得到si则si是合法的。
问每个结点的字母要怎么选择才能满足所有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个顺序结点
假设第i个结点对应sj路径上的第k个顺序结点
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
1如果sj,k!=ci,1,设置条件pi=true∣∣wj=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 2如果sj,k!=ci,2,设置条件pi=false∣∣wj=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 3如果sj,∣sj∣−k+1!=ci,1,设置条件pi=true∣∣wj=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 3如果sj,∣sj∣−k+1!=ci,2,设置条件pi=false∣∣wj=false
#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
*/
本人码农,希望通过自己的分享,让大家更容易学懂计算机知识。