李超线段树
就是区间cmin一次函数,只不过被搬到树上来了。李超线段树标记永久化搞一搞就行了。O(nlog^3)
具体地。每一个节点表示一个区间,存储一个一次函数,表示在该区间的优势一次函数,即能取到最小值的一次函数。更新的时候一整条往下扫一下即可。
大年初一凌晨敲道农题压压惊
UPD(2017.4.8):震惊!突然发现自己下面的代码是错的。注意下面代码里标记的地方。正确的代码附在后面了。话说这题数据好像不够强啊,这个都没卡掉……
#include<cstdio>
#include<algorithm>
#define N 100005
using namespace std;
namespace runzhe2000
{
typedef long long ll;
int in()
{
int r = 0, p = 0; char c = getchar();
for(; c < '0' || c > '9'; c = getchar()) if(c == '-') p = 1;
for(; c >='0' && c <='9'; c = getchar()) r = r * 10 + c - '0';
return p ? -r : r;
}
ll ans, dis[N];
int n, m, last[N], ecnt, top[N], siz[N], son[N], fa[N], dep[N], pos[N], repos[N], timer;
struct edge{int next, to, val;}e[N<<1];
struct seg{ll K, B, mi;}t[N<<2];
void addedge(int a, int b, int c){e[++ecnt] = (edge){last[a], b, c}; last[a] = ecnt;}
void dfs1(int x)
{
dep[x] = dep[fa[x]] + 1; siz[x] = 1;
for(int i = last[x]; i; i = e[i].next)
{
int y = e[i].to; if(y == fa[x])continue;
fa[y] = x; dis[y] = dis[x] + e[i].val; dfs1(y);
siz[x] += siz[y]; if(siz[y] > siz[son[x]]) son[x] = y;
}
}
void dfs2(int x)
{
top[x] = son[fa[x]] == x ? top[fa[x]] : x;
pos[x] = ++timer; repos[timer] = x;
if(son[x]) dfs2(son[x]); else return;
for(int i = last[x]; i; i = e[i].next)
{
int y = e[i].to;
if(y == fa[x] || y == son[x]) continue;
dfs2(y);
}
}
int get_lca(int a, int b)
{
for(; top[a] != top[b]; )
{
if(dep[top[a]] < dep[top[b]]) swap(a, b);
a = fa[top[a]];
}
return dep[a] < dep[b] ? a : b;
}
void build(int x, int l, int r)
{
t[x].K = 0; t[x].B = t[x].mi = 123456789123456789ll;
if(l == r) return;
int mid = (l+r)>>1;
build(x<<1,l,mid); build(x<<1|1,mid+1,r);
}
void pushup(int x, int l, int r)
{
ll f_lef = t[x].K * dis[repos[l]] + t[x].B, f_rig = t[x].K * dis[repos[r]] + t[x].B;
if(l == r)t[x].mi = f_lef;
else t[x].mi = min(min(f_lef, f_rig), min(t[x<<1].mi, t[x<<1|1].mi));
}
void modi(int x, int l, int r, ll K, ll B)
{
ll f1_lef = t[x].K * dis[repos[l]] + t[x].B, f1_rig = t[x].K * dis[repos[r]] + t[x].B;
ll f2_lef = K * dis[repos[l]] + B, f2_rig = K * dis[repos[r]] + B;
if(f2_lef >= f1_lef && f2_rig >= f1_rig) return;
if(f2_lef <= f1_lef && f2_rig <= f1_rig) {t[x].K = K; t[x].B = B;}
else {int mid = (l+r)>>1; modi(x<<1,l,mid,K,B); modi(x<<1|1,mid+1,r,K,B);}//这里把一个一次函数递归进两个子树,虽然在当前节点该一次函数有一定的优势区间,似乎能返回。但递归下去之后,子树的一次函数是不确定的,因此可能进一步递归下去而不能返回,可能退化为O(n)。
pushup(x,l,r);
}
void modi(int x, int l, int r, int ql, int qr, ll K, ll B)
{
if(ql <= l && r <= qr){modi(x,l,r,K,B);return;}
int mid = (l+r)>>1;
if(ql <= mid) modi(x<<1,l,mid,ql,qr,K,B);
if(mid < qr) modi(x<<1|1,mid+1,r,ql,qr,K,B);
pushup(x,l,r);
}
void query(int x, int l, int r, int ql, int qr)
{
if(ql <= l && r <= qr)
{
ans = min(ans, t[x].mi);
return;
}
int L = max(l, ql), R = min(r, qr);
ans = min(ans, min((ll)t[x].K*dis[repos[L]]+t[x].B, (ll)t[x].K*dis[repos[R]]+t[x].B));
int mid = (l+r)>>1;
if(ql <= mid)query(x<<1,l,mid,ql,qr);
if(mid < qr)query(x<<1|1,mid+1,r,ql,qr);
}
void main()
{
n = in(); m = in();
for(int i = 1, a, b, c; i < n; i++)
{
a = in(), b = in(), c = in();
addedge(a, b, c); addedge(b, a, c);
}
dfs1(1); dfs2(1);
build(1,1,n);
for(int _m = 1, opt, s, t, S, T, a, b; _m <= m; _m++)
{
opt = in();
if(opt == 1)
{
S = in(), T = in(), a = in(), b = in();
int lca = get_lca(S, T); ll K, B;
K = -a; B = b + (ll)a * dis[S];
for(s = S; top[s] != top[lca]; s = fa[top[s]])modi(1,1,n,pos[top[s]], pos[s], K, B); modi(1,1,n,pos[lca], pos[s], K, B);
K = a; B = b + (ll)a * (dis[S] - 2*dis[lca]);
for(t = T; top[t] != top[lca]; t = fa[top[t]])modi(1,1,n,pos[top[t]], pos[t], K, B); modi(1,1,n,pos[lca], pos[t], K, B);
}
else
{
s = in(), t = in();
int lca = get_lca(s, t); ans = 123456789123456789ll;
for(; top[s] != top[lca]; s = fa[top[s]]) query(1,1,n,pos[top[s]], pos[s]); query(1,1,n,pos[lca], pos[s]);
for(; top[t] != top[lca]; t = fa[top[t]]) query(1,1,n,pos[top[t]], pos[t]); query(1,1,n,pos[lca], pos[t]);
printf("%lld\n",ans);
}
}
}
}
int main()
{
runzhe2000::main();
}
以下正解,注意标注的地方
#include<cstdio>
#include<algorithm>
#define N 200005
#define cmin(u,v) ((u)>(v)?(u)=(v):0)
using namespace std;
namespace runzhe2000
{
typedef double db;
typedef long long ll;
const ll INF = 123456789123456789ll;
ll read()
{
ll r = 0; int p = 0; char c = getchar();
for(; c < '0' || c > '9'; c = getchar()) c == '-' ? p = 1 : 0;
for(; c >='0' && c <='9'; r = r*10+c-'0', c = getchar());
return p ? -r : r;
}
ll ans, dis[N];
int n, m, ecnt, timer, last[N], dep[N], siz[N], son[N], top[N], fa[N], beg[N], rebeg[N];
struct edge{int next, to; ll val;}e[N<<1];
void addedge(int a, int b, ll c)
{
e[++ecnt] = (edge){last[a], b, c};
last[a] = ecnt;
}
void dfs1(int x)
{
dep[x] = dep[fa[x]] + 1; siz[x] = 1;
for(int i = last[x]; i; i = e[i].next)
{
int y = e[i].to; if(y == fa[x]) continue;
fa[y] = x; dis[y] = dis[x] + e[i].val; dfs1(y); siz[x] += siz[y];
siz[y] > siz[son[x]] ? son[x] = y : 0;
}
}
void dfs2(int x)
{
top[x] = son[fa[x]]==x?top[fa[x]] : x;
rebeg[beg[x] = ++timer] = x; if(son[x]) dfs2(son[x]);
for(int i = last[x]; i; i = e[i].next)
{
int y = e[i].to; if(y == fa[x] || y == son[x]) continue;
dfs2(y);
}
}
int get_lca(int a, int b)
{
for(; top[a] != top[b]; )
{
if(dep[top[a]] < dep[top[b]]) b = fa[top[b]];
else a = fa[top[a]];
}
return dep[a] < dep[b] ? a : b;
}
struct seg
{
ll k, b, v;
}t[N*5];
void build(int x, int l, int r)
{
t[x].k = 0; t[x].v = t[x].b = INF; if(l == r) return; int mid = (l+r)>>1;
build(x<<1,l,mid); build(x<<1|1,mid+1,r);
}
void pushup(int x, int l, int r)
{
ll &k1 = t[x].k, &b1 = t[x].b;
t[x].v = min(k1 * dis[rebeg[l]] + b1, k1 * dis[rebeg[r]] + b1);
if( l == r )return;
t[x].v = min(t[x].v, min(t[x<<1].v, t[x<<1|1].v));
}
void do_modi(int x, int l, int r, ll k, ll b)
{
ll &k1 = t[x].k, &b1 = t[x].b;
if(k1 * dis[rebeg[l]] + b1 <= k * dis[rebeg[l]] + b && k1 * dis[rebeg[r]] + b1 <= k * dis[rebeg[r]] + b) return;
if(k1 * dis[rebeg[l]] + b1 > k * dis[rebeg[l]] + b && k1 * dis[rebeg[r]] + b1 > k * dis[rebeg[r]] + b)
{ k1 = k; b1 = b; pushup(x,l,r); return; }
int mid = (l+r)>>1;
db x_mid = (dis[rebeg[mid]] + dis[rebeg[mid+1]]) / 2.0;
//注意只能递归进一个子树
if(k1 * x_mid + b1 > k * x_mid + b) swap(k, k1), swap(b, b1);
if((db)(b-b1)/(k1-k) < x_mid) do_modi(x<<1,l,mid,k,b);
else do_modi(x<<1|1,mid+1,r,k,b);
pushup(x,l,r);
}
void modi(int x, int l, int r, int ql, int qr, ll k, ll b)
{
if(ql <= l && r <= qr) return do_modi(x,l,r,k,b);
int mid = (l+r)>>1;
if(ql <= mid) modi(x<<1,l,mid,ql,qr,k,b);
if(mid < qr) modi(x<<1|1,mid+1,r,ql,qr,k,b);
pushup(x,l,r);
}
void add(int x, int y, ll k, ll b)
{
for(; top[x] != top[y]; x = fa[top[x]])
modi(1,1,n,beg[top[x]],beg[x],k,b);
modi(1,1,n,beg[y],beg[x],k,b);
}
void query(int x, int l, int r, int ql, int qr)
{
ans = min(ans, t[x].k * dis[rebeg[max(l, ql)]] + t[x].b);
ans = min(ans, t[x].k * dis[rebeg[min(r, qr)]] + t[x].b);
if(ql <= l && r <= qr) {cmin(ans, t[x].v); return;}
int mid = (l+r)>>1;
if(ql <= mid) query(x<<1, l, mid, ql, qr);
if(mid < qr) query(x<<1|1,mid+1, r, ql, qr);
}
void ask(int x, int y)
{
for(; top[x] != top[y]; x = fa[top[x]])
query(1,1,n,beg[top[x]],beg[x]);
query(1,1,n,beg[y],beg[x]);
}
void main()
{
n = read(); m = read();
for(int i = 1, a, b, c; i < n; i++)
{
a = read(); b = read(); c = read();
addedge(a, b, c); addedge(b, a, c);
}
dfs1(1); dfs2(1); build(1,1,n);
for(int i = 1, s, t, z; i <= m; i++)
{
if(read() == 1) //add
{
ll a, b;
s = read(); t = read(); a = read(); b = read();
z = get_lca(s, t);
add(s, z, -a, b+a*dis[s]);
add(t, z, a, b-a*(dis[z]*2-dis[s]));
}
else // ask
{
s = read(); t = read(); z = get_lca(s, t);
ans = INF;
ask(s, z);
ask(t, z);
printf("%lld\n",ans);
}
}
}
}
int main()
{
runzhe2000::main();
}