「CSP-S 2019」树上的数(树上推理)
Posted coldchair
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「CSP-S 2019」树上的数(树上推理)相关的知识,希望对你有一定的参考价值。
过了这么久看看自己要多久才能切这题,发现还是想歪了一次。
先考虑暴力的做法。
还是贪心的逐位确定,逐位确定判有没有解,相当于下面的问题:
树上有一些路径,一条路径表示要把(x)的数字换到(y)去,问有没有解。
对于一条路径(p[1],p[2],…,p[m]),限制如下:
1.((p[1],p[2]))是(p[1])的所有相邻边中时间最小的。
2.((p[[m-1],p[m]))是(p[m])的所有相邻边中时间最大的。
3.(forall iin [1,m-2],(p[i],p[i+1])和(p[i+1],p[i+2]))在(p[i+1])的所有相邻边中时间是相邻的(前小于后)。
发现所有限制都是对于一个点的相邻边的,因为是树,所以不同点之间的相临边限制不会影响。
那么只看每个点的相邻边是否有满足条件的解。
暴力的做法就是先把3限制的缩成若干段段(段内要合法),然后若(T(i)<T(j)),则(i->j)连边,看有没有环就好了。
仔细思考,除了3限制就只有最小最大限制,那么可以总结为以下几个限制:
1.每一段是合法的(不能有反向边、跨越边)
2.min前面不能有小于它的
3.max后面不能有大于它的
4.min、max若处于一段,则这一段的长度必须是中转点的度数
复杂度没怎么变,一共要check(O(n^2))次,每次(O(n))。
考虑对于每一位,不去枚举它选什么,而是先求它能选什么,再从中选最小的。
假设第(?)位的起点是(x),以它为根,dfs一遍,处理上面四条限制,就可以求出每个点能不能作为终点了!
需要并查集维护段,时间复杂度:(O(T*n^2α))
Code:
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i < _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("
")
using namespace std;
const int N = 4005;
int T;
int n, x, y, a[N], ia[N];
int fi[N], nt[N], to[N], tot;
void link(int x, int y) {
nt[++ tot] = fi[x], to[tot] = y, fi[x] = tot;
}
void cl() {
fo(i, 1, n) fi[i] = 0;
tot = 1;
}
int m;
int us[N], b[N];
int l[N], r[N], f[N], siz[N], du[N];
int F(int x) {
return f[x] == x ? x : (f[x] = F(f[x]));
}
void bin(int x, int y) {
if(F(x) != F(y)) {
x = f[x], y = f[y];
siz[y] += siz[x];
f[x] = y;
}
}
int pd(int x, int y) {
return F(x) == F(y);
}
int mi[N], mx[N];
int rt, fa[N], fq[N], ok[N], ok_ed[N];
void dg(int x) {
if(x != rt) {
if(fa[x] == rt) {
int y = fa[x], u = fq[x];
if(ok[x])
if(mi[y] && mi[y] != u) ok[x] = 0; else
if(l[u]) ok[x] = 0; else
if(pd(u, mx[y]) && siz[F(u)] != du[y]) ok[x] = 0;
} else {
int y = fa[x];
int u = fq[y] ^ 1, v = fq[x];
if(ok[x])
if(r[u] && r[u] != v) ok[x] = 0; else
if(l[v] && l[v] != u) ok[x] = 0; else
if(pd(u, v) && r[u] != v) ok[x] = 0; else
if(pd(mx[y], u)) ok[x] = 0; else
if(pd(mi[y], v)) ok[x] = 0; else
if(pd(mi[y], u) && pd(mx[y], v) && !pd(u, v) && siz[F(u)] + siz[F(v)] != du[y]) ok[x] = 0;
}
int u = fq[x] ^ 1;
ok_ed[x] = 1;
if(mx[x] && mx[x] != u) ok_ed[x] = 0; else
if(r[u]) ok_ed[x] = 0; else
if(pd(u, mi[x]) && siz[F(u)] != du[x]) ok_ed[x] = 0;
}
for(int i = fi[x]; i; i = nt[i]) {
int y = to[i]; if(y == fa[x]) continue;
fa[y] = x;
fq[y] = i;
ok[y] = ok[x];
dg(y);
}
}
void gao(int x, int y) {
mx[y] = fq[y] ^ 1;
for(; fa[y] != x; y = fa[y]) {
int u = fq[fa[y]] ^ 1, v = fq[y];
r[u] = v; l[v] = u;
bin(u, v);
}
mi[x] = fq[y];
}
void work() {
cl();
scanf("%d", &n);
fo(i, 1, n) {
scanf("%d", &x);
a[x] = i; ia[i] = x;
}
fo(i, 1, n) du[i] = 0;
fo(i, 1, n - 1) {
scanf("%d %d", &x, &y);
link(x, y); link(y, x);
du[x] ++, du[y] ++;
}
fo(i, 1, tot) {
f[i] = i; siz[i] = 1;
l[i] = r[i] = mi[i] = mx[i] = 0;
}
fo(i, 1, n) us[i] = 0;
for(m = 1; m <= n; m ++) {
rt = ia[m];
ok[rt] = 1;
fo(i, 1, n) fa[i] = 0;
dg(rt);
fo(i, 1, n) if(rt != i && !us[i] && ok[i] && ok_ed[i]) {
us[i] = 1;
b[m] = i;
gao(rt, i);
break;
}
}
if(n == 1) b[1] = 1;
fo(i, 1, n) pp("%d ", b[i]); hh;
}
int main() {
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
scanf("%d", &T);
fo(ii, 1, T) {
work();
}
}
以上是关于「CSP-S 2019」树上的数(树上推理)的主要内容,如果未能解决你的问题,请参考以下文章
JZOJ6431luoguP5658CSP-S2019括号树