hdu 6962 I love tree 线段树维护二次函数

传送门

文章目录

题意:

给你 n n n个点的一颗树,有 m m m次询问,每次询问有两个操作:
( 1 ) (1) (1) [ a , b ] [a,b] [a,b]路径上的点依次加上 1 2 , 2 2 , 3 2 , . . . , l e n 2 , l e n = p a t h ( a , b ) 1^2,2^2,3^2,...,len^2,len=path(a,b) 12,22,32,...,len2,len=path(a,b)
( 2 ) (2) (2)询问 x x x点的值。

思路:

先考虑在线性的情况下如何实现两个操作。
不难发现,直接修改的瓶颈是不能确保每次修改的都一样,但是发现我们最终查询的时候只是单点查询,所以我们可以考虑将每个点的值作为变量,让后维护常量。
通过以上分析,我们可以考虑将操作 1 1 1转换成二次函数,比如当前要加区间 [ l , r ] [l,r] [l,r],那么就相当于加上一个 ( x − l ) 2 (x-l)^2 (xl)2的二次函数,其中 x ∈ [ l + 1 , r + 1 ] x\in [l+1,r+1] x[l+1,r+1],将其展开 x 2 + l 2 − 2 ∗ x ∗ l x^2+l^2-2*x*l x2+l22xl,显然我们可以分别对三个系数单独维护,即 1 , l 2 , 2 l 1,l^2,2l 1,l2,2l,也就是线段树区间修改的过程,之后查询的时候乘上对应的位置即可。
那么在树上怎么求呢?显然只需要一个树剖即可,树剖将其分成 l o g n logn logn段区间每段区间都是连续的,所以注意一下细节直接写就好啦。

//#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")
//#pragma GCC optimize(2)
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<cmath>
#include<cctype>
#include<vector>
#include<set>
#include<queue>
#include<algorithm>
#include<sstream>
#include<ctime>
#include<cstdlib>
#include<random>
#include<cassert>
#define X first
#define Y second
#define L (u<<1)
#define R (u<<1|1)
#define pb push_back
#define mk make_pair
#define Mid ((tr[u].l+tr[u].r)>>1)
#define Len(u) (tr[u].r-tr[u].l+1)
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define db puts("---")
using namespace std;

//void rd_cre() { freopen("d://dp//data.txt","w",stdout); srand(time(NULL)); }
//void rd_ac() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//AC.txt","w",stdout); }
//void rd_wa() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//WA.txt","w",stdout); }

typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;

const int N=1000010,mod=1e9+7,INF=0x3f3f3f3f;
const double eps=1e-6;

int n;
vector<int>v[N];
int fa[N],depth[N],se[N],son[N],dfn[N],tot,top[N];

struct Seg {
	struct Node {
		int l,r;
		LL sum,lazy;
	}tr[N<<2];
	
	void pushup(int u) {
		tr[u].sum=tr[L].sum+tr[R].sum;
	}
	
	void pushdown(int u) {
		LL lazy=tr[u].lazy; tr[u].lazy=0;
		tr[L].sum+=lazy*Len(L); tr[L].lazy+=lazy;
		tr[R].sum+=lazy*Len(R); tr[R].lazy+=lazy;
	}
	
	void build(int u,int l,int r) {
		tr[u]={l,r};
		if(l==r) return;
		build(L,l,Mid); build(R,Mid+1,r);
	}
	
	void change(int u,int l,int r,LL sum) {
		if(tr[u].l>=l&&tr[u].r<=r) {
			tr[u].sum+=Len(u)*sum;
			tr[u].lazy+=sum;
			return ;
		}
		pushdown(u);
		if(l<=Mid) change(L,l,r,sum);
		if(r>Mid) change(R,l,r,sum);
		// pushup(u);
	}
	
	LL query(int u,int pos) {
		if(tr[u].l==tr[u].r) return tr[u].sum;
		pushdown(u);
		if(pos<=Mid) return query(L,pos);
		else return query(R,pos);
		// pushup(u);
	}
}t1,t2,t3;

void dfs1(int u,int f) {
	depth[u]=depth[f]+1; fa[u]=f;
	se[u]=1;
	for(auto x:v[u]) {
		if(x==f) continue;
		dfs1(x,u);
		se[u]+=se[x];
		if(se[x]>se[son[u]]) son[u]=x;
	}
}

void dfs2(int u,int t) {
	top[u]=t; dfn[u]=++tot;
	if(son[u]) dfs2(son[u],t);
	for(auto x:v[u]) {
		if(x==son[u]||x==fa[u]) continue;
		dfs2(x,x);
	}
}

void change(int x,int y,int cnt) {
	int l=1,r=cnt;
	while(top[x]!=top[y]) {
		if(depth[top[x]]>depth[top[y]]) {
		 	LL add=dfn[x]+l; l+=depth[x]-depth[top[x]]+1;
		 	t1.change(1,dfn[top[x]],dfn[x],1);
		 	t2.change(1,dfn[top[x]],dfn[x],add*add);
		 	t3.change(1,dfn[top[x]],dfn[x],add);
		 	x=fa[top[x]];
		} else {
			int now=depth[y]-depth[top[y]]+1;
			LL add=dfn[top[y]]+(r-now-1); r-=now;
		 	t1.change(1,dfn[top[y]],dfn[y],1);
		 	t2.change(1,dfn[top[y]],dfn[y],add*add);
		 	t3.change(1,dfn[top[y]],dfn[y],add);
		 	y=fa[top[y]];
		}
	}
	if(depth[x]>depth[y]) {
	 	LL add=dfn[x]+l; l+=depth[x]-depth[y]+1;
	 	t1.change(1,dfn[y],dfn[x],1);
	 	t2.change(1,dfn[y],dfn[x],add*add);
	 	t3.change(1,dfn[y],dfn[x],add);
	} else {
		int now=depth[y]-depth[x]+1;
		LL add=dfn[x]-(r-now+1); r-=now;
	 	t1.change(1,dfn[x],dfn[y],1);
	 	t2.change(1,dfn[x],dfn[y],add*add);
	 	t3.change(1,dfn[x],dfn[y],add);
	}
}

int lca(int x,int y) {
	while(top[x]!=top[y]) {
		if(depth[top[x]]<depth[top[y]]) swap(x,y);
		x=fa[top[x]];
	}
	if(depth[x]<depth[y]) return x;
	else return y;
}

int main()
{
//	ios::sync_with_stdio(false);
//	cin.tie(0);

	scanf("%d",&n);
	for(int i=1;i<=n-1;i++) {
		int a,b; scanf("%d%d",&a,&b);
		v[a].pb(b); v[b].pb(a);
	}
	dfs1(1,0); dfs2(1,1);
	t1.build(1,1,n); t2.build(1,1,n); t3.build(1,1,n);
	int m; scanf("%d",&m);
	while(m--) {
		int op,x,y;
		scanf("%d%d",&op,&x);
		if(op==1) {
			scanf("%d",&y);
			change(x,y,depth[x]+depth[y]-2*depth[lca(x,y)]+1);
		} else {
			//if(x==2) cout<<t3.query(1,dfn[x])<<"**"<<endl;
			printf("%lld\n",t1.query(1,dfn[x])*dfn[x]*dfn[x]+t2.query(1,dfn[x])-2*dfn[x]*t3.query(1,dfn[x]));
		}
	}
	
	


	return 0;
}
/*

*/









评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值