zoukankan      html  css  js  c++  java
  • 数据结构优化建图

    数据结构优化建图

    有的时候我们需要将编号在 ([L, R]) 中的每一个点都向编号在 ([L', R']) 中的每一个点连边。这时暴力连边会使复杂度达到 (O(n^2m)) 的级别,因此我们需要对区间分块统一处理。

    我们需要建立一棵入树和一棵出树。入树中的节点表示某些边可以连到区间中的任意点;出树的节点表示区间中的所有点都能向外连某些边。

    首先我们对这些块与块之间进行处理:

    • 入树的所有父亲向儿子连0边(能到达([1, 10]),必然能到达 ([1, 5])([6, 10]));

    • 出树的所有儿子向父亲连0边(能从 ([1, 5]) 中的某些点出发,必然能从 ([1, 10]) 的某些点出发);

    • 入树的所有点向出树的对应点连0边(能到达([1, 10])的所有点,必然能从([1, 10])中的某些点出发)。

    (简而言之,入树是一棵外向树,出树是一棵内向树,树树连边)

    然后当我们连边 ([l, r] -> [l', r'])的时候,我们把出树中的 ([l, r]) 分成若干段,连向一个中转节点 (p_i),然后将 (p_i) 连向入树中的 ([l',r']) 所含的若干段。

    最后把整体当作一个图来处理即可。能保证最终的所有叶子节点的信息正确。(所有操作从入点开始做)

    因为入树都向出树对应节点连无关紧要的边,使得出树的信息最终会被入树信息更新,但是不保证入树信息被出树信息更新。因此我们只需从入树开始,就能保证最终两棵树信息一致。

    例题:CF786B Legacy

    题意:支持一对多连边,求单源最短路。

    模板题。直接找到入树中的 (s) 节点开始跑最短路即可。最终入树中的叶子节点和出树中的叶子节点的信息都是正确的。

    例题:P3588 [POI2015]PUS

    题意:已知一个序列的部分数,以及知道某些区间中的某些数的最小值比剩下的所有数都大。要求判无解或构造序列。

    差分约束+线段树优化建边。

    my record

    一些降低编程复杂度的小技巧:

    • 可以把建入树和建出树分开来写,记录一下一棵树的节点数 (tot), 然后连树间的边就直接((cur - tot) ~ ~ ->~ ~ cur) 就好。

    • 建树的时候可以直接记录一下每个位置对应的叶子节点编号,方便单点查询。

    void build_i(int L, int R, int &cur) {
    	cur = ++ttot;
    	if (L == R)	return i_p[L] = cur, h[cur] = a[L], isg[cur] = a[L] > 0, void();
    	int mid = (L + R) >> 1;
    	build_i(L, mid, ls[cur]), build_i(mid + 1, R, rs[cur]);
    	addedge(cur, ls[cur], 0); addedge(cur, rs[cur], 0);
    }
    
    void build_o(int L, int R, int &cur) {
    	cur = ++ttot;
    	addedge(cur - tcnt, cur, 0);
    	if (L == R)	return o_p[L] = cur, h[cur] = a[L], isg[cur] = a[L] > 0, void();
    	int mid = (L + R) >> 1;
    	build_o(L, mid, ls[cur]), build_o(mid + 1, R, rs[cur]);
    	addedge(ls[cur], cur, 0), addedge(rs[cur], cur, 0);
    }
    

    P5025 [SNOI2017]炸弹

    题意:求一维坐标上炸弹引爆个数

    实际上要求的是有向图上的某点出发能经过的点的权值和。

    需要单点向区间点连边。因此需要线段树优化建图。

    缩点后跑dp 这样会算重,但是洛谷数据太毒了,bzoj这样可以AC,洛谷86pts

    由于最终答案一定为一段连续的闭区间,因此可以记录mx和mn,然后dfs+记搜。

    然后就56pts了

    好像这里建两棵树要WA,看来有的时候建一棵树要更好一些。(只有单点连向多点的时候)

    弹跳

    这个比较例外。这个题利用K-D Tree空间消耗小的优点,实现单点对矩形的最短路优化建图。

    这也提示我们可以灵活运用各种数据结构来优化建图。只要方法合法,且包含所有合法情况,就可以考虑去这么写。

    Ants

    这个是线段树优化建图跑 2-SAT,需要用到前缀优化建2-SAT的思想。

    前缀优化建图通常解决的问题是,一个集合中如果选了其中一个点,那么其余点都不能选。它大概长这个样子:

    前缀优化建图 2-sat

    其中两排红点为辅助点,分别表示后缀点和前缀点。黑、棕色方点为原始节点。

    线段树优化建图的问题则是,一个线段树节点内存着若干个点,如果选了其中一个点,那么其余点都不能选,且线段树的祖先节点内的点和子树内的点也不能被选。它大概长这个样子:

    线段树优化建图 2-sat

    大概就是把父子的串给连起来。

    需要注意的是,这种方法的节点数特别多,点数和边数都是 (O(n log n)) 级别的。这道题是树剖套线段树,所以是 (O(n log^2 n)) 的。

    Flags

    这个是普通的线段树优化建图跑2-sat,新建的点和新建的边并不符合对称性,只不过与朴素建图等价。

    模板:单源最短路(调试用)

    void build(int L, int R, int &cur) {
    	cur = ++ttot;
    	if (L == R)	return ;
    	int mid = (L + R) >> 1;
    	build(L, mid, ls[cur]), build(mid + 1, R, rs[cur]);
    	if (type)	addedge(cur, ls[cur], 0), addedge(cur, rs[cur], 0);
    	else	addedge(ls[cur], cur, 0), addedge(rs[cur], cur, 0);
    }
    
    int ptot;
    void Out(int L, int R, int l, int r, int p, int w, int cur) {
    	if (l <= L && R <= r) {
    		addedge(cur, p, 0);
    		return ;
    	}
    	int mid = (L + R) >> 1;
    	if (l <= mid)	Out(L, mid, l, r, p, w, ls[cur]);
    	if (r > mid)	Out(mid + 1, R, l, r, p, w, rs[cur]);
    }
    void In(int L, int R, int l, int r, int p, int w, int cur) {
    	if (l <= L && R <= r) {
    		addedge(p, cur, w);
    		return ;
    	}
    	int mid = (L + R) >> 1;
    	if (l <= mid)	In(L, mid, l, r, p, w, ls[cur]);
    	if (r > mid)	In(mid + 1, R, l, r, p, w, rs[cur]);
    }
    
    inline void Link(int l, int r, int l_, int r_, int w) {
    	++ptot;
    	Out(1, n, l, r, ptot, w, root_o);
    	In(1, n, l_, r_, ptot, w, root_i);
    }
    
    int find(int L, int R, int pos, int cur) {
    	if (L == R)	return cur;
    	int mid = (L + R) >> 1;
    	if (pos <= mid)	return find(L, mid, pos, ls[cur]);
    	return find(mid + 1, R, pos, rs[cur]);
    }
    
    struct node{
    	int cur;
    	ll val;
    	bool operator <(const node a) const {
    		return val > a.val;
    	}
    };
    priority_queue<node> q;
    
    ll dis[N];
    bool vis[N];
    
    inline void dij() {
    	int st = find(1, n, s, root_i);
    	memset(dis, 0x3f, sizeof(dis));
    	
    	q.push((node){st, 0}); dis[st] = 0;
    	while (!q.empty()) {
    		int cur = q.top().cur; q.pop();
    		if (vis[cur])	continue;
    		vis[cur] = true;
    		for (register int i = head[cur]; i; i = e[i].nxt) {
    			int to =e[i].to;
    			if (dis[to] > dis[cur] + e[i].val) {
    				dis[to] = dis[cur] + e[i].val;
    				q.push((node){to, dis[to]});
    			}
    		}
    	}
    }
    
    void print(int L, int R, int cur) {
    	if (L == R) {
    		printf("%lld ", dis[cur] >= inf ? -1ll : dis[cur]);
    		return ;
    	}
    	int mid = (L + R) >> 1;
    	print(L, mid, ls[cur]);
    	print(mid + 1, R, rs[cur]);
    }
    
    int main() {
    	read(n), read(m), read(s);
    	type = 1, build(1, n, root_i); int tot = ttot;
    	type = 0, build(1, n, root_o);
    	for (register int i = 1; i <= tot; ++i)
    		addedge(i, i + tot, 0);
    	
    	ptot = ttot;
    	for (...) Link(l, r, l_, r_);
    	dij();
    	print(1, n, root_i);
    	puts("");
    	return 0;
    }
    
  • 相关阅读:
    faster with MyISAM tables than with InnoDB or NDB tables
    w-BIG TABLE 1-toSMALLtable @-toMEMORY
    Indexing and Hashing
    MEMORY Storage Engine MEMORY Tables TEMPORARY TABLE max_heap_table_size
    controlling the variance of request response times and not just worrying about maximizing queries per second
    Variance
    Population Mean
    12.162s 1805.867s
    situations where MyISAM will be faster than InnoDB
    1920.154s 0.309s 30817
  • 原文地址:https://www.cnblogs.com/JiaZP/p/14315538.html
Copyright © 2011-2022 走看看