• 「CSP-S 2019」树上的数(树上推理)


    https://loj.ac/problem/3210

    过了这么久看看自己要多久才能切这题,发现还是想歪了一次。

    先考虑暴力的做法。

    还是贪心的逐位确定,逐位确定判有没有解,相当于下面的问题:
    树上有一些路径,一条路径表示要把(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();
    	}
    }
    
  • 相关阅读:
    你都这么拼了,面试官TM怎么还是无动于衷
    js中string转map的方法
    如何使用jmeter做一个功能的性能测试
    如何看待远程办公?
    vue.js指令v-for使用以及下标索引的获取
    v-charts x轴字体斜显示
    Linux-(inotify-tools&rsync)
    Linux-(type,vim)
    zab协议
    数据库的规范一览
  • 原文地址:https://www.cnblogs.com/coldchair/p/12766596.html
Copyright © 2020-2023  润新知