BZOJ4515 - [Sdoi2016]游戏(李超线段树,树剖)

题意

Alice 和 Bob 在玩一个游戏。
游戏在一棵有 n 个点的树上进行。最初,每个点上都只有一个数字,那个数字是 123456789123456789。
有时,Alice 会选择一条从 s 到 t 的路径,在这条路径上的每一个点上都添加一个数字。对于路径上的一个点 r,
若 r 与 s 的距离是 dis,那么 Alice 在点 r 上添加的数字是 a×dis+b。有时,Bob 会选择一条从 s 到 t 的路径。
他需要先从这条路径上选择一个点,再从那个点上选择一个数字。
Bob 选择的数字越小越好,但大量的数字让 Bob 眼花缭乱。Bob 需要你帮他找出他能够选择的最小的数字。

题解

很容易看出是树剖+李超线段树,但是小细节很多,比较容易挂。
李超线段树的横轴为dfn编号对应节点的树上前缀和,然后插入线段加上一定的偏移。插入的时候注意不同方向的影响。
李超线段树要求区间最小值,要加个pushup(其实这个板子我是抄的)

#include <bits/stdc++.h>

#define endl '\n'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define FILE freopen("3.in","r",stdin),freopen("res.txt","w",stdout)
#define FI freopen("3.txt","r",stdin)
#define FO freopen("res.txt","w",stdout)
#define mp make_pair
#define seteps(N) fixed << setprecision(N)
typedef long long ll;

using namespace std;
/*-----------------------------------------------------------------*/

ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
#define INF 123456789123456789

const int N = 1e6 + 10;
const double eps = 1e-5;

/***************树剖******************/
int son[N], siz[N], h[N], nxt[N], dep[N], p[N], fa[N], top[N], cnt, rnk[N], dfn[N], wi[N], si;
ll sum[N];
int n, m;

void dfs1(int o) {
son[o] = -1;
siz[o] = 1;
for (int j = h[o]; j; j = nxt[j])
  if (!dep[p[j]]) {
    dep[p[j]] = dep[o] + 1;
    sum[p[j]] = sum[o] + wi[j];
    fa[p[j]] = o;
    dfs1(p[j]);
    siz[o] += siz[p[j]];
    if (son[o] == -1 || siz[p[j]] > siz[son[o]]) son[o] = p[j];
  }
}

void dfs2(int o, int t) {
top[o] = t;
cnt++;
dfn[o] = cnt;
rnk[cnt] = o;
if (son[o] == -1) return;
dfs2(son[o], t);  // 优先对重儿子进行 DFS,可以保证同一条重链上的点 DFS 序连续
for (int j = h[o]; j; j = nxt[j])
  if (p[j] != son[o] && p[j] != fa[o]) dfs2(p[j], p[j]);
}

void add(int u, int v, int w) {
  si++;
  nxt[si] = h[u];
  h[u] = si;
  p[si] = v;
  wi[si] = w;
}

/******************树剖*******************/

/****************超哥线段树****************/
ll k[N], b[N];
struct LiChaoTree {
	#define ls rt << 1
	#define rs rt << 1 | 1

	int t[N << 2]; ll Mn[N << 2];

	inline void Init() { t[0] = 1; Mn[0] = INF; }
	inline void update(int rt) { Mn[rt] = min(Mn[rt], min(Mn[ls], Mn[rs])); }
	inline ll calc(int x, int id) { return k[id] * (ll)sum[rnk[x]] + b[id]; }
	inline void build(int rt, int l, int r) {
		t[rt] = 1; Mn[rt] = INF;
		if (l == r) return;
		int mid = (l + r) >> 1;
		build(ls, l, mid); build(rs, mid + 1, r);
		update(rt);
	}

	void chkmin(ll &t, ll x) {
       t = min(t, x);
	}
	inline void insert(int rt, int l, int r, int L, int R, int id) {
		int mid = (l + r) >> 1;
		if (L <= l && r <= R) {
			if (calc(l, id) <= calc(l, t[rt]) && calc(r, id) <= calc(r, t[rt])) {
				t[rt] = id;
				chkmin(Mn[rt], min(calc(l, t[rt]), calc(r, t[rt])));
				return;
			}
			if (calc(l, id) >= calc(l, t[rt]) && calc(r, id) >= calc(r, t[rt])) return;
			if (calc(mid, id) <= calc(mid, t[rt])) swap(t[rt], id);
			insert(ls, l, mid, L, R, id);
			insert(rs, mid + 1, r, L, R, id);
			chkmin(Mn[rt], min(calc(l, t[rt]), calc(r, t[rt])));
			update(rt);
		}
		if (L <= mid) insert(ls, l, mid, L, R, id);
		if (R > mid) insert(rs, mid + 1, r, L, R, id);
		update(rt);
	}
	inline ll Query(int rt, int l, int r, int L, int R) {
		if (L <= l && r <= R) return Mn[rt];
		int mid = (l + r) >> 1; ll ret = INF;
		if (b[t[rt]] != INF) ret = min(calc(max(l, L), t[rt]), calc(min(r, R), t[rt]));
		if (L <= mid) chkmin(ret, Query(ls, l, mid, L, R));
		if (R > mid) chkmin(ret, Query(rs, mid + 1, r, L, R));
		return ret;
	}

} T;
/****************超哥线段树****************/

/******************询问*******************/

ll length(int u, int v) {
  ll res = 0;
  while (top[u] != top[v]) {
      if (dep[top[u]] > dep[top[v]]) {
          int f = fa[top[u]];
          res += sum[u] - sum[f];
          u = f;
      } else {
          int f = fa[top[v]];
          res += sum[v] - sum[f];
          v = f;
      }
  }
  res += abs(sum[u] - sum[v]);
  return res;
}

int ssi = 1;
void modify(int u, int v, ll a, ll c) {
  int st = u;
  int ed = v;
  ll len = length(u, v);
  while (top[u] != top[v]) {
      if (dep[top[u]] > dep[top[v]]) {
          int l = dfn[top[u]], r = dfn[u];
          // 两种情况方向不一样 
          k[++ssi]= -a;
          b[ssi] = c + a * sum[st];
          T.insert(1, 1, n, l, r, ssi);
          u = fa[top[u]];
      } else {
          int l = dfn[top[v]], r = dfn[v];
          // 两种情况方向不一样 
          k[++ssi]= a;
          b[ssi] = c + a * (len - sum[ed]);
          T.insert(1, 1, n, l, r, ssi);
          v = fa[top[v]];
      }
  }
  if(dep[u] > dep[v]) {
      int l = dfn[v], r = dfn[u];
      k[++ssi]= -a;
      b[ssi] = c + a * sum[st];
      T.insert(1, 1, n, l, r, ssi);
  } else {
      int l = dfn[u], r = dfn[v];
      k[++ssi]= a;
      b[ssi] = c + a * (len - sum[ed]);
      T.insert(1, 1, n, l, r, ssi);

  }
}

ll qmin(int u, int v) {
  ll res = INF;
  while (top[u] != top[v]) {
      if (dep[top[u]] > dep[top[v]]) {
          int l = dfn[top[u]], r = dfn[u];
          res = min(res, T.Query(1, 1, n, l, r));//(1, n, l, r, 1));
          u = fa[top[u]];
      } else {
          int l = dfn[top[v]], r = dfn[v];
          res = min(res, T.Query(1, 1, n, l, r));
          v = fa[top[v]];
      }
  }
  int l = dfn[v], r = dfn[u];
  if(l > r) swap(l ,r);
  res = min(res, T.Query(1, 1, n, l, r));
  return res;
}
/******************询问*******************/


int main() {
  //FILE;
  dep[1] = 1;
  cin >> n >> m;
  T.Init();
  T.build(1, 1, n);
  k[1] = 0, b[1] = INF;
  for(int i = 1; i < n; i++) {
      int u, v, w;
      cin >> u >> v >> w;
      add(u, v, w);
      add(v, u, w);
  }
  dfs1(1);
  dfs2(1, 1);
  while(m--) {
      int ty;
      cin >> ty;
      if(ty == 1) {
          ll s, t, a, b;
          cin >> s >> t >> a >> b;
          modify(s, t, a, b);
      } else {
          int s, t;
          cin >> s >> t;
          cout << qmin(s, t) << endl;
      }
  }
}
posted @ 2020-11-25 22:06  limil  阅读(117)  评论(0编辑  收藏  举报