輕量樹上問題選做

輕量的樹上問題。

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) << '\n';
		}
	}
	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 << '\n';
	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] << '\n';
	}
	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] << '\n';
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章