• 轻量树上问题选做


    轻量的树上问题。

    NOIP2013 货车运输

    首先有结论:一张无向图上两点之间的最优的瓶颈路是最值生成树上的两点之间的路。

    于是就可以最大生成树+链最值做, 只考虑码量就倍增就可以了。

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1e5 + 23, M = 5e5 + 23, K = 19;
    
    int n, m;
    
    struct edge{ int x, y, z;} e[M];
    bool cmp(edge s1, edge s2) { return s1.z > s2.z;}
    int fa[N];
    int fid(int x) {return fa[x] == x ? x : fa[x] = fid(fa[x]);}
    int ecnt, hd[N], nt[N*2+233], vr[N*2+233], w[N*2+233];
    void ad(int u, int v, int c) { nt[++ecnt]=hd[u], hd[u]=ecnt; vr[ecnt]=v, w[ecnt]=c;}
    void ad_e(int x, int y, int z) { ad(x,y,z), ad(y,x,z);}
    
    int f[K][N], mi[K][N], dep[N];
    void dfs(int x, int F) {
    	for(int i=hd[x]; i; i=nt[i]) {
    		int y = vr[i];
    		if(y == F) continue;
    		f[0][y] = x, mi[0][y] = w[i], dep[y] = dep[x] + 1;
    		dfs(y, x);
    	}
    }
    
    int ques(int x, int y) {
    	int ret = 2147483647;
    	if(dep[x] > dep[y]) swap(x, y);
    	for(int k=K-1; k>=0; --k) if(dep[f[k][y]] >= dep[x]) ret = min(ret, mi[k][y]), y = f[k][y];
    	if(x == y) return ret;
    	for(int k=K-1; k>=0; --k)
    		if(f[k][x] != f[k][y]) {
    			ret = min(ret, min(mi[k][x], mi[k][y]));
    			x = f[k][x], y = f[k][y];
    		}
    	return min(ret, min(mi[0][x], mi[0][y]));
    }
    
    int main()
    {
    //	cout << (1<<14);
    	scanf("%d%d", &n, &m);
    	for(int i=1; i<=m; ++i) {
    		scanf("%d%d%d", &e[i].x, &e[i].y, &e[i].z);
    	}
    	sort(e+1, e+1+m, cmp);
    	for(int i=1; i<=n; ++i) fa[i] = i;
    	for(int i=1; i<=m; ++i) {
    		int x = fid(e[i].x), y = fid(e[i].y);
    		if(x == y) continue;
    		fa[x] = y;
    		ad_e(e[i].x, e[i].y, e[i].z);
    	}
    	for(int i=1; i<=n; ++i) if(!dep[i]) {
    		dep[i] = 1; dfs(i, 0);
    	}
    	for(int k=1; k<K; ++k) {
    		for(int i=1; i<=n; ++i) {
    			f[k][i] = f[k-1][f[k-1][i]];
    			mi[k][i] = min(mi[k-1][i], mi[k-1][f[k-1][i]]);
    		}
    	}
    	int Q;
    	scanf("%d", &Q);
    	while(Q--)
    	{
    		int x,y;
    		scanf("%d%d", &x, &y);
    		if(fid(x) != fid(y)) {
    			puts("-1");
    		} else {
    			cout << ques(x, y) << '
    ';
    		}
    	}
    	return 0;
    }
    

    NOIP2015 运输计划

    二分答案, 对所有边 i 统计其被路径权大于答案的路径经过的次数 timei, 记路径权大于答案的路径总数为 cnt, 若没有 time = cnt 的边, 则 false, 反之找出所有 time = cnt 的边, 记录最大边权 mx, 然后看一下删去 mx 是否能够满足要求就行了。

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 3e5 + 233;
    
    int n, m, s[N], t[N], A[N], w[N];
    int ecnt, hd[N], nt[N*2+1], vr[N*2+1], val[N*2+1];
    void ad(int x, int y, int z) {nt[++ecnt]=hd[x], hd[x]=ecnt, vr[ecnt]=y, val[ecnt]=z;}
    
    int siz[N], dep[N], fa[N], son[N], tp[N], dis[N], faval[N];
    void dfs1(int x, int F, int D) {
    	siz[x] = 1, dep[x] = D, fa[x] = F;
    	for(int i=hd[x]; i; i=nt[i]) {
    		int y = vr[i];
    		if(y == F) continue;
    		dis[y] = dis[x] + val[i];
    		faval[y] = val[i];
    		dfs1(y, x, D + 1);
    		siz[x] += siz[y];
    		son[x] = siz[son[x]] < siz[y] ? y : son[x];
    	}
    }
    void dfs2(int x, int T) {
    	tp[x] = T;
    	if(son[x]) dfs2(son[x], T);
    	for(int i=hd[x]; i; i=nt[i]) {
    		int y = vr[i];
    		if(y == fa[x] || y == son[x]) continue;
    		dfs2(y, y);
    	}
    }
    
    int lca(int x, int y) {
    	while(tp[x] != tp[y]) dep[tp[x]] > dep[tp[y]] ? x = fa[tp[x]] : y = fa[tp[y]];
    	return dep[x] > dep[y] ? y : x;
    }
    
    int nanachi[N];
    void dfs3(int x) {
    	for(int i=hd[x]; i; i=nt[i]) {
    		int y = vr[i];
    		if(y == fa[x]) continue;
    		dfs3(y);
    		nanachi[x] += nanachi[y];
    	}
    }
    
    bool check(int lim) {
    	int cnt = 0, mxval = -2147483600;
    	for(int i=1; i<=n; ++i) nanachi[i] = 0;
    	for(int i=1; i<=m; ++i)
    		if(w[i] > lim) {
    			++cnt; mxval = max(mxval, w[i]);
    			++nanachi[s[i]];
    			++nanachi[t[i]];
    			nanachi[A[i]] -= 2;
    		}
    	dfs3(1);
    	int mx = -2147483600;
    	for(int i=1; i<=n; ++i) if(nanachi[i] == cnt) mx = max(mx, faval[i]);
    	if(mx == -2147483600) return false;
    //	cout << "# " << mxval - mx << ' ' << lim << '
    ';
    	return (mxval - mx <= lim);
    }
    
    int main()
    {
    	scanf("%d%d", &n, &m);
    	for(int i=1; i<n; ++i) {
    		int x,y,z; scanf("%d%d%d", &x, &y, &z); ad(x,y,z), ad(y,x,z);
    	}
    	dfs1(1, 0, 1);
    	dfs2(1, 1);
    	int l=0, r=0;
    	for(int i=1; i<=m; ++i) {
    		scanf("%d%d", &s[i], &t[i]);
    		A[i] = lca(s[i], t[i]);
    		w[i] = dis[s[i]] + dis[t[i]] - 2 * dis[A[i]];
    		r = max(r, w[i]);
    //		cout << A[i] << " # " << w[i] << '
    ';
    	}
    	while(l != r) {
    		int mid = (l + r) >> 1;
    		check(mid) ? r = mid : l = mid + 1;
    	}
    	cout << l;
    	return 0;
    }
    

    NOIP2016 天天爱跑步

    首先把路径 [s,t] 拆为:[s,lcas,t), [lcas,t,t] 两段, 分别称为向根型路和向叶型路。

    对于向叶型路, 看成时间倒流的向根型路即可。

    那么现在就只有两类向根型路了, 在先前突破时间的限制之后, 现在突破人的限制, 一条向根型路可以差分成一条一个人走到根的路和一条负一个人走到根的路。

    对于走到根的路, 可以轻松的转化为子树内的值域数值查询问题, 开个桶即可, 即, 本子树的桶 = 遍历完本子树的桶 - 开始遍历本子树前的桶。

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 3e5 + 233;
    
    int n, m, w[N], s[N], t[N], A[N];
    int ecnt, hd[N], nt[N*2+1], vr[N*2+1];
    void ad(int x, int y) { nt[++ecnt]=hd[x],hd[x]=ecnt,vr[ecnt]=y;}
    
    int dep[N], siz[N], fa[N], son[N], tp[N];
    void dfs1(int x, int F, int D) {
    	siz[x] = 1, fa[x] = F, dep[x] = D;
    	for(int i=hd[x]; i; i=nt[i]) {
    		int y = vr[i];
    		if(y == F) continue;
    		dfs1(y, x, D + 1);
    		siz[x] += siz[y];
    		son[x] = siz[son[x]] > siz[y] ? son[x] : y;
    	}
    }
    void dfs2(int x, int T) {
    	tp[x] = T;
    	if(son[x]) dfs2(son[x], T);
    	for(int i=hd[x]; i; i=nt[i]) {
    		int y = vr[i];
    		if(y == fa[x] || y == son[x]) continue;
    		dfs2(y, y);
    	}
    }
    int lca(int x, int y) {
    	while(tp[x] != tp[y]) dep[tp[x]] > dep[tp[y]] ? x = fa[tp[x]] : y = fa[tp[y]];
    	return dep[x] > dep[y] ? y : x;
    }
    int up(int x, int D, int Top) {
    	while(dep[tp[x]] > D) x = fa[tp[x]];
    	return dep[tp[x]] == D ? tp[x] : son[Top];
    }
    
    struct edge{
    	edge *nt;
    	pair<int,int> comm;
    	edge(edge *n, pair<int,int> v) : nt(n), comm(v) {
    	}
    	edge() {
    	}
    };
    
    struct link {
    	int ecnt;
    	edge e[N*2+233], *hd[N];
    	void init(int n) {
    		ecnt = 0;
    		for(int i=0; i<=n; ++i) hd[i] = NULL;
    	}
    	void ad(int x, pair<int,int> val) {
    		e[++ecnt] = edge(hd[x], val);
    		hd[x] = &e[ecnt];
    	}
    } cmd;
    
    int ans[N];
    int tong[N*2+233];
    map<int,int> tong2;
    
    void calc1(int x) {
    	ans[x] -= tong[w[x] + dep[x]];
    	for(int i=hd[x]; i; i=nt[i]) {
    		int y = vr[i];
    		if(y == fa[x]) continue;
    		calc1(y);
    	}
    	for(edge *i = cmd.hd[x]; i; i = i->nt) {
    		pair<int,int> comm = i->comm;
    		tong[comm.first] += comm.second;
    	}
    	ans[x] += tong[w[x] + dep[x]];
    }
    
    void calc2(int x) {
    	if(tong2.count(w[x] - dep[x])) ans[x] -= tong2[w[x] - dep[x]];
    	for(int i=hd[x]; i; i=nt[i]) {
    		int y = vr[i];
    		if(y == fa[x]) continue;
    		calc2(y);
    	}
    	for(edge *i = cmd.hd[x]; i; i = i->nt) {
    		pair<int,int> comm = i->comm;
    		if(!tong2.count(comm.first)) tong2[comm.first] = comm.second;
    		else tong2[comm.first] += comm.second;
    	}
    	if(tong2.count(w[x] - dep[x])) ans[x] += tong2[w[x] - dep[x]];
    }
    
    int main()
    {
    	scanf("%d%d", &n, &m);
    	for(int i=1; i<n; ++i) {
    		int x,y; scanf("%d%d", &x, &y); ad(x, y), ad(y, x);
    	}
    	for(int i=1; i<=n; ++i) scanf("%d", &w[i]);
    	dfs1(1, 0, 1), dfs2(1, 1);
    	for(int i=1; i<=m; ++i) {
    		scanf("%d%d", &s[i], &t[i]); A[i] = lca(s[i], t[i]);
    	}
    	
    	// algorithm - main !!! ================================================
    	
    	cmd.init(n);
    	for(int i=1; i<=m; ++i) if(s[i] != A[i]) {
    //		int D = up(s[i], dep[A[i]]+1, A[i]);
    		// b[i] + dep[i] == w[x] + dep[x]
    		cmd.ad(s[i], make_pair(dep[s[i]], 1));
    		cmd.ad(A[i], make_pair(dep[s[i]], -1));
    	}
    	calc1(1);
    	
    	cmd.init(n);
    	for(int i=1; i<=m; ++i) {
    		// A[i] -> t[i]
    		// time[i] - (dep[i] - dep[x]) == w[x]
    		// time[i] - dep[i] == w[x] - dep[x]
    		int timei = dep[s[i]] + dep[t[i]] - 2 * dep[A[i]];
    		cmd.ad(t[i], make_pair(timei - dep[t[i]], 1));
    		cmd.ad(fa[A[i]], make_pair(timei - dep[t[i]], -1));
    	}
    	calc2(1);
    	
    	for(int i=1; i<=n; ++i) cout << ans[i] << ' ';
    	return 0;
    }
    

    SCOI2015 情报传递

    主要是询问一条链上危险度 >C 的点的总数。

    C, 即 当前时间-开始时间 > C, 即 开始时间 < 当前时间-C, 相当于在某个时间点询问已经开始的数量, 这样直接离线就行了。

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 2e5 + 233;
    
    int n, m, root;
    int ecnt, hd[N], nt[N], vr[N];
    void ad(int x, int y) {nt[++ecnt]=hd[x],hd[x]=ecnt,vr[ecnt]=y;}
    
    int dep[N], siz[N], fa[N], son[N], tp[N];
    int in[N], out[N], dfntot;
    void dfs1(int x, int F, int D) {
    	dep[x] = D, siz[x] = 1, fa[x] = F;
    	for(int i=hd[x]; i; i=nt[i]) {
    		int y = vr[i];
    		dfs1(y, x, D + 1);
    		siz[x] += siz[y];
    		son[x] = siz[son[x]] > siz[y] ? son[x] : y;
    	}
    }
    void dfs2(int x, int T) {
    	tp[x] = T;
    	in[x] = ++dfntot;
    	if(son[x]) dfs2(son[x], T);
    	for(int i=hd[x]; i; i=nt[i]) {
    		int y = vr[i];
    		if(y == son[x]) continue;
    		dfs2(y, y);
    	}
    	out[x] = dfntot;
    }
    
    int lca(int x, int y) {
    	while(tp[x] != tp[y]) dep[tp[x]] > dep[tp[y]] ? x = fa[tp[x]] : y = fa[tp[y]];
    	return dep[x] > dep[y] ? y : x; 
    }
    
    struct cmd{
    	int id, nid;
    	int op, a, b;
    } comm[N];
    bool cmp(cmd s1, cmd s2) {
    	return s1.nid == s2.nid ? s1.op < s2.op : s1.nid < s2.nid;
    }
    int ans[N], sum[N];
    
    int t[N];
    void ins(int x) {
    	for(;x<=n;x+=(x&(-x))) ++t[x];
    }
    int ask(int x) { int ret = 0;
    	for(;x;x-=(x&(-x))) ret += t[x];
    	return ret;
    }
    
    int road_ask(int x, int y) {
    	int ret = 0;
    	while(tp[x] != tp[y]) {
    		if(dep[tp[x]] < dep[tp[y]]) swap(x,y);
    		ret += (ask(in[x]) - ask(in[tp[x]]-1));
    		x = fa[tp[x]];
    	}
    	if(dep[x] < dep[y]) swap(x,y);
    	ret += (ask(in[x]) - ask(in[y]-1));
    	return ret;
    }
    
    int main()
    {
    	scanf("%d", &n);
    	for(int i=1; i<=n; ++i) {
    		int f; scanf("%d",&f);
    		if(f) ad(f, i); else root = i;
    	}
    	scanf("%d", &m);
    	for(int i=1; i<=m; ++i) {
    		scanf("%d", &comm[i].op);
    		comm[i].id = i;
    		if(comm[i].op == 1)
    		{
    			int c;
    			scanf("%d%d%d", &comm[i].a, &comm[i].b, &c);
    			// µ±Ç°Ê±¼ä - c > ¿ªÊ¼Ê±¼ä;
    			comm[i].nid = i - c;
    		}
    		if(comm[i].op == 2)
    		{
    			scanf("%d", &comm[i].a);
    			comm[i].nid = i;
    		}
    	}
    	sort(comm+1, comm+1+m, cmp);
    	
    	dfs1(root, 0, 1), dfs2(root, root);
    	memset(ans, -1, sizeof ans);
    	for(int i=1; i<=m; ++i) {
    		if(comm[i].op == 2) ins(in[comm[i].a]);
    		else
    			{
    				int x = comm[i].a, y = comm[i].b, A = lca(x,y);
    				ans[comm[i].id] = road_ask(x, y);
    				sum[comm[i].id] = dep[x] + dep[y] - dep[A] - dep[fa[A]];
    			}
    	}
    	for(int i=1; i<=m; ++i) if(~ans[i]) cout << sum[i] << ' ' << ans[i] << '
    ';
    	return 0;
    }
    
  • 相关阅读:
    json数据解析转文本方法
    百度HttpV3版本图片识别
    项目用Socket网络框架+Protobuf
    各类数据类型的转换类
    异形按钮点击触发
    通过名字找物体工具
    任意图形工具
    Debug日志可视化
    fread不能读完整个文件
    生产者消费者问题——C++ windows版 多生产者多消费者的队列实现
  • 原文地址:https://www.cnblogs.com/tztqwq/p/14296178.html
Copyright © 2020-2023  润新知