第一次接触到这么神奇的线段树。
首先树的形态不改变所以数链剖分+线段树建树。对于线段树上的每一个点,我们保证其最多保存一条直线,如果存在第二条直线,那么两条直线“占据空间”较小的一个一定可以下传到它的某个儿子,而这个节点只存占据空间较大的一个即可。
查询的时候要用到永久化(好像是叫这个奇怪的名字)思想,一边向下查一边比较存放在当前节点的直线能否更新最小值。
/************************************************************** Problem: 4515 User: RicardoWang Language: C++ Result: Accepted Time:21920 ms Memory:38232 kb ****************************************************************/ #include<cstdlib> #include<cstdio> #include<iostream> #include<cstring> #include<cmath> #include<algorithm> #include<queue> #include<vector> using namespace std; #define oo 123456789123456789ll #define maxn 200005 void _read(int &x){x=0; char ch=getchar(); bool flag=false;while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}if(flag)x=-x; return ;} void _readLL(long long &x){x=0; char ch=getchar(); bool flag=false;while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}if(flag)x=-x; return ;} struct edge{int to,d,next;}e[maxn*2]; int edge_ct,head[maxn]; void add(int x,int y,int z){e[++edge_ct]=(edge){y,z,head[x]};head[x]=edge_ct;e[++edge_ct]=(edge){x,z,head[y]};head[y]=edge_ct;} int n,m,son[maxn],sz[maxn],d[maxn],tp[maxn],tr[maxn],f[maxn],_time,_tr[maxn],fd[maxn]; long long dis[maxn]; void DFS_1(int now,int fa) { sz[now]=1; son[now]=0; d[now]=d[fa]+1; f[now]=fa; dis[now]=dis[fa]+fd[now]; for(int id=head[now];id;id=e[id].next){if(e[id].to==fa)continue;fd[e[id].to]=e[id].d;DFS_1(e[id].to,now);sz[now]+=sz[e[id].to];if(sz[e[id].to]>sz[son[now]])son[now]=e[id].to;} return ; } void DFS_2(int now,int top) { tr[now]=++_time;tp[now]=top; if(son[now])DFS_2(son[now],top); for(int id=head[now];id;id=e[id].next){if(e[id].to==f[now] || e[id].to==son[now])continue; DFS_2(e[id].to,e[id].to);}return ; } void Init(){int x,y,z;_read(n);_read(m);for(int i=1;i<n;i++){_read(x);_read(y);_read(z);add(x,y,z);}DFS_1(1,0);DFS_2(1,1);return ;} int np,rt,chi[2*maxn][2];bool tag[maxn*2]; long long d_first[2*maxn],dsum[2*maxn],minv[maxn]; void build(int &now,int L,int R){now=++np; tag[now]=false; minv[now]=oo;if(L==R){d_first[now]=dsum[now]=fd[_tr[L]];return;}int m=(L+R)>>1;build(chi[now][0],L,m);build(chi[now][1],m+1,R);d_first[now]=d_first[chi[now][0]];dsum[now]=dsum[chi[now][0]]+dsum[chi[now][1]];} int LCA(int x,int y){ while(tp[x]!=tp[y]){if(d[tp[x]]>=d[tp[y]])x=f[tp[x]];else y=f[tp[y]];} return d[x]>d[y] ? y:x;} long long getsum(int now,int L,int R,int x,int y) { if(x<=L && R<=y)return dsum[now]; long long t1=0,t2=0; int m=(L+R)>>1; if(x<=m)t1=getsum(chi[now][0],L,m,x,y); if(y>m)t2=getsum(chi[now][1],m+1,R,x,y); return t1+t2; } long long _A[maxn*2],_B[maxn*2]; void pushtag(int now,int L,int R,long long A,long long B) { if(!tag[now]){ tag[now]=true; _A[now]=A; _B[now]=B ;} else { if(A==_A[now]) { _B[now]=min(_B[now],B); } else { if(A>_A[now]){swap(A,_A[now]);swap(B,_B[now]);} long long T=(B-_B[now])/(_A[now]-A); while(T*(_A[now]-A)<=(B-_B[now]))T++; if(T<=d_first[now]) { _A[now]=A; _B[now]=B; } else if(T>dsum[now]) { } else { if(T<=dsum[chi[now][0]]) { pushtag(chi[now][0],L,(L+R)/2,_A[now],_B[now]); _A[now]=A; _B[now]=B; } else { pushtag(chi[now][1],(L+R)/2+1,R,A,B+A*dsum[chi[now][0]]); } } } } return ; } long long update(int now,int L,int R,int x,int y,long long A,long long B) { if(x<=L && R<=y) { pushtag(now,L,R,A,B); minv[now]=min(minv[now],A>=0 ? A*d_first[now]+B : A*dsum[now]+B); return dsum[now]; } int m=(L+R)>>1; long long tmp=0; if(x<=m)tmp=update(chi[now][0],L,m,x,y,A,B); if(y>m)tmp=tmp+update(chi[now][1],m+1,R,x,y,A,B+tmp*A); minv[now]=min(minv[now],min(minv[chi[now][0]],minv[chi[now][1]])); return tmp; } long long get(int now,int L,int R,int x) { if(!tag[now])return oo; else return _A[now]*getsum(rt,1,n,L,x)+_B[now]; } long long query(int now,int L,int R,int x,int y) { long long ans=oo; if(tag[now] && x<=R && y>=L)ans=min(ans,_A[now]>=0? get(now,L,R,max(x,L)):get(now,L,R,min(y,R))); if(x<=L && R<=y) { return min(ans,minv[now]); } int m=(L+R)>>1; if(x<=m)ans=min(ans,query(chi[now][0],L,m,x,y)); if(y>m)ans=min(ans,query(chi[now][1],m+1,R,x,y)); return ans; } void op1(int x,int y,long long a,long long b) { int z=LCA(x,y); long long dissum=0; while(tp[x]!=tp[z]) { dissum+=getsum(rt,1,n,tr[tp[x]],tr[x]); update(rt,1,n,tr[tp[x]],tr[x],-a,dissum*a+b); x=f[tp[x]]; } dissum+=getsum(rt,1,n,tr[z],tr[x]); update(rt,1,n,tr[z],tr[x],-a,dissum*a+b); dissum-=fd[z]; dissum+=dis[y]-dis[z]; while(tp[y]!=tp[z]) { dissum-=getsum(rt,1,n,tr[tp[y]],tr[y]); update(rt,1,n,tr[tp[y]],tr[y],a,dissum*a+b); y=f[tp[y]]; } if(y!=z){dissum-=getsum(rt,1,n,tr[son[z]],tr[y]);update(rt,1,n,tr[son[z]],tr[y],a,dissum*a+b);} return ; } char s[35];int cc; void out(long long x) { if(!x)putchar('0'); if(x<0){x=-x;putchar('-');} cc=0; while(x){s[++cc]=x%10+'0'; x=x/10;} while(cc){putchar(s[cc]);cc--;} putchar('\n'); return ; } void op2(int x,int y) { int z=LCA(x,y); long long ans=oo; while(tp[x]!=tp[z]) { ans=min(ans,query(rt,1,n,tr[tp[x]],tr[x])); x=f[tp[x]]; } ans=min(ans,query(rt,1,n,tr[z],tr[x])); while(tp[y]!=tp[z]) { ans=min(ans,query(rt,1,n,tr[tp[y]],tr[y])); y=f[tp[y]]; } ans=min(ans,query(rt,1,n,tr[z],tr[y])); out(ans); return ; } void work() { for(int i=1;i<=n;i++)_tr[tr[i]]=i; build(rt,1,n); char op;int s,t;long long a,b; int cct=0; for(int i=1;i<=m;i++) { op=getchar(); while(op!='1'&&op!='2')op=getchar(); if(op=='1') { if(i==2458) { int hh; hh++; } _read(s); _read(t); _readLL(a); _readLL(b); op1(s,t,a,b); } else { cct++; if(i==2459) { int hh; hh++; } _read(s); _read(t);/* s=110;t=110;printf("%d ",i);*/op2(s,t); } } return ; } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); Init(); work(); return 0; }