• Luogu2483 [SDOI2010]魔法猪学院(可并堆)


    俞鼎力大牛的课件

    对于原图以 (t) 为根建出任意一棵最短路径树 (T),即反着从 (t) 跑出到所有点的最短路 (dis)

    它有一些性质:

    性质1:

    对于一条 (s)(t) 的路径的边集 (P),去掉 (P) 中和 (T) 的交集,记为 (P')

    那么 (P') 对于中任意相邻(从 (s)(t) 的顺序)的两条边 (e,f),满足 (f) 的起点在 (T) 中为 (e) 的终点的祖先或者为相同点。

    因为 (P)(e,f) 之间由树边相连或者直接相连。

    性质2:

    对于不在 (T) 中的边 (e) ,设 (u) 为起点,(v) 为终点,(w)为权值。

    定义 (Delta_e=dis_v+w-dis_u),即选这条边的路径和最短路的长度的差

    (L_P) 表示路径长度,则有

    [L_P=dis_s+sum_{ein p'}Delta_e ]

    这很显然。

    性质3:

    对于满足性质 (1)(P')的定义的边集 (S),有且仅有一条 (s)(t) 的路径的边集 (P),使得 (P'=S)

    因为树 (T) 上的两个点之间有且仅有一条路径。

    问题转化

    求第 (k) 小的满足性质 (1)(P')的定义的边集

    算法

    用小根堆维护边集 (P)

    初始 (P) 为空集(实际上只要维护边集当前尾部的边的起点是哪一个就好了,空集即 (s)

    每次取出最小权值的边集 (P),设当前尾部的边的起点为 (x)

    有两种方法可以得到一个新的边集:

    1.替换 (x) 为起点的这条边为一条刚好大于等于它的非树边。

    2.尾部接上一条起点为以 (x) 为起点的这条边的终点在 (T) 中祖先(包括自己)连出去的所有非树边的最小边。

    然后就是怎么维护祖先出去的所有非树边的最小边:

    显然可以从祖先转移过来,直接可并堆即可。

    又因为要保留每个点的信息,所以合并的时候可持久化即可

    和线段树合并的可持久化一样,然后就可以过了。

    建议可以看一看课件

    # include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    template <class Num> inline void Cmax(Num &x, const Num y) {
    	x = y > x ? y : x;
    }
    
    template <class Num> inline void Cmin(Num &x, const Num y) {
    	x = y < x ? y : x;
    }
    
    const int maxn(5005);
    const int maxm(2e5 + 5);
    const double eps(1e-8);
    
    int n, m, first[maxn], cnt, vis[maxn], rt[maxn], tot, cov[maxm << 1], ans, fa[maxn];
    double se, e, dis[maxn];
    priority_queue < pair <double, int> > q;
    
    struct Heap {
    	int ls, rs, dis, ed;
    	double w;
    } tr[maxm * 20];
    
    struct Edge {
    	int to, next;
    	double w;
    } edge[maxm << 1];
    
    inline void Add(int u, int v, double w) {
    	edge[cnt] = (Edge){v, first[u], w}, first[u] = cnt++;
    	edge[cnt] = (Edge){u, first[v], w}, first[v] = cnt++;
    }
    
    inline int NewNode(double w, int ed) {
    	int x = ++tot;
    	tr[x].w = w, tr[x].dis = 1, tr[x].ed = ed;
    	return x;
    }
    
    int Merge(int x, int y) {
    	if (!x || !y) return x + y;
    	if (tr[x].w - tr[y].w >= eps) swap(x, y);
    	int p = ++tot;
    	tr[p] = tr[x], tr[p].rs = Merge(tr[p].rs, y);
    	if (tr[tr[p].ls].dis < tr[tr[p].rs].dis) swap(tr[p].ls, tr[p].rs);
    	tr[p].dis = tr[tr[x].rs].dis + 1;
    	return p;
    }
    
    void Dfs(int u) {
    	vis[u] = 1;
    	for (int e = first[u], v; e != -1; e = edge[e].next)
    		if (e & 1) {
    			double w = edge[e].w;
    			if (fabs(dis[u] + w - dis[v = edge[e].to]) < eps && !vis[v])
    				fa[v] = u, cov[e ^ 1] = 1, Dfs(v);
    		}
    }
    
    int main() {
    	memset(first, -1, sizeof(first));
    	memset(dis, 127, sizeof(dis));
    	scanf("%d%d%lf", &n, &m, &se);
    	for (int i = 1, u, v; i <= m; ++i) scanf("%d%d%lf", &u, &v, &e), Add(u, v, e);
    	dis[n] = 0, q.push(make_pair(0, n));
    	while (!q.empty()) {
    		int u = q.top().second;
    		q.pop();
    		if (vis[u]) continue;
    		vis[u] = 1;
    		for (int e = first[u]; ~e; e = edge[e].next)
    			if (e & 1) {
    				int v = edge[e].to;
    				if (dis[v] - (dis[u] + edge[e].w) >= eps)
    					q.push(make_pair(-(dis[v] = dis[u] + edge[e].w), v));
    			}
    	}
    	for (int i = 1; i <= n; ++i) vis[i] = 0;
    	Dfs(n);
    	for (int e = 0, u, v; e < cnt; e += 2)
    		if (!cov[e]) {
    			u = edge[e ^ 1].to, v = edge[e].to;
    			if (dis[u] == dis[0] || dis[v] == dis[0]) continue;
    			rt[u] = Merge(rt[u], NewNode(dis[v] + edge[e].w - dis[u], v));
    		}
    	for (int i = 1; i <= n; ++i) q.push(make_pair(-dis[i], i));
    	for (int i = 1, u; i <= n; ++i) {
    		u = q.top().second, q.pop();
    		if (fa[u]) rt[u] = Merge(rt[u], rt[fa[u]]);
    	}
    	if (dis[1] - se < eps) se -= dis[1], ++ans;
    	if (rt[1]) q.push(make_pair(-tr[rt[1]].w, rt[1]));
    	while (!q.empty()) {
    		int ed = q.top().second;
    		double cur = q.top().first, w = dis[1] - cur;
    		if (w - se >= eps) break;
    		q.pop(), se -= w, ++ans;
    		for (int i = 0; i < 2; ++i) {
    			int nxt = i ? tr[ed].rs : tr[ed].ls;
    			if (nxt) q.push(make_pair(cur + tr[ed].w - tr[nxt].w, nxt));
    		}
    		if (rt[tr[ed].ed]) q.push(make_pair(cur - tr[rt[tr[ed].ed]].w, rt[tr[ed].ed]));
    	}
    	printf("%d
    ", ans);
    	return 0;
    }
    
  • 相关阅读:
    解决Metasploit中shell乱码的问题
    AWVS扫描器的用法
    AWVS扫描器的用法
    AppScan扫描器的用法
    AppScan扫描器的用法
    Nessus扫描器的使用
    IPC$共享和其他共享(C$、D$)
    IPC$共享和其他共享(C$、D$)
    Windows中动态磁盘管理
    Windows中动态磁盘管理
  • 原文地址:https://www.cnblogs.com/cjoieryl/p/9895982.html
Copyright © 2020-2023  润新知