杭电2021多校第二场 HDU 6962 I love tree

  • Post author:
  • Post category:其他



题面



题目传送门(HDU 6962 I love tree)

Given a tree with n nodes and q operations, there are two kinds of operations.

1 a b : for a chain <a, b>, increase the value of x2 to the x-th point on this chain

for example the chain from a to b=(x1,x2,x3,x4,x5),after the operation,x1+=1,x2+=4,x3+=9,x4+=16,x5+=25

2 x :asks the value of x-th node

翻译:两种操作

1:一棵无根树,在节点



x

x






x





到节点



y

y






y





的最近链上,从



x

x






x





开始,每次加



i

2

(

1

<

=

i

<

=

l

e

n

)

i^2(1<=i<=len)







i










2









(


1




<






=








i




<






=








l


e


n


)








l

e

n

len






l


e


n





为链的长度

2:查询节点



x

x






x





的值是多少

很容易想到

树链剖分+线段树

难点在于

如何维护区间加平方


树链剖分这一块接下来细说,现在考虑如何维护




a

L

+

1

2

,

a

L

+

i

+

2

2

,

a

R

+

(

R

L

+

1

)

2

a_L+1^2,a_{L+i}+2^2,a_{R}+(R-L+1)^2







a










L




















+









1










2









,





a











L


+


i





















+









2










2









,





a











R





















+








(


R













L




+








1



)










2













考虑对区间



[

L

,

R

]

[L, R]






[


L


,




R


]





进行上面的操作

第一个数加1的平方,第二个数加2的平方,以此类推

发现,加1和加2的平方都是由,当前位置



i

i






i





和起始操作位置



l

l






l





决定的

为了方便讨论令



l

=

L

1

l = L-1






l




=








L













1





(其实就是为了好分析加的是多少的平方)

也就是对于



a

i

a_i







a










i





















来说,



a

i

+

(

i

l

)

2

a_i+(i-l)^2







a










i




















+








(


i













l



)










2
















a

i

a_i







a










i





















初始为0,对后面的式子进行分解



(

i

l

)

2

=

i

2

+

l

2

2

i

l

(i-l)^2 = i^2 +l^2-2*i*l






(


i













l



)










2











=









i










2











+









l










2




















2













i













l






假如我们要求某一个点



a

i

a_i







a










i





















的值

此时会等于



a

i

+

i

2

+

l

2

2

i

l

a_i + \sum{i^2} + \sum{l^2} -\sum{2*i*l}







a










i




















+















i










2












+















l










2



























2









i









l







由于



a

i

a_i







a










i





















初始为0,



i

2

i^2







i










2
















2

i

2*i






2













i





也已知,从求和公式提出

最后变为了



i

2

1

+

l

2

2

i

l

i^2*\sum{1} + \sum{l^2} -2*i*\sum{l}







i










2


























1





+















l










2





















2













i



















l







从而分析,通过线段树或者树状数组维护,每个



i

i






i





的这三个求和的值,也就是区间和

另外由上面公式推及,



l

=

L

1

l=L-1






l




=








L













1





,区间



[

L

,

R

]

[L,R]






[


L


,




R


]





加平方(顺序加,如1,4,9,16),就要在区间



[

L

,

R

]

[L,R]






[


L


,




R


]





维护



1

,

(

L

1

)

2

,

L

1

\sum{1},\sum{(L-1)^2,\sum{L-1}}












1



,










(


L









1



)










2









,










L









1







问题

假如要倒着加呢如(16, 9, 4, 1)

同样考虑区间



[

L

,

R

]

[L,R]






[


L


,




R


]









a

i

+

(

R

i

+

1

)

2

a_i+(R-i+1)^2







a










i




















+








(


R













i




+








1



)










2












,令



r

=

R

+

1

r=R+1






r




=








R




+








1









(

r

i

)

2

=

(

i

r

)

2

(r-i)^2=(i-r)^2






(


r













i



)










2











=








(


i













r



)










2













这样又能由上面的过程推到答案了

也就是说,倒着加就让参数



l

l






l





变为



r

r






r





即可实现,复用同一个线段树


树链剖分


我们知道树链剖分的



t

o

p

[

u

]

top[u]






t


o


p


[


u


]









u

u






u









d

f

n

dfn






d


f


n





连续的一段,可以使用线段树进行维护

类似于找lca的过程,过程中仔细考虑一下,此过程是顺得加,还是倒着加,时刻记录顺的加了多少,倒着加了多少的左右端点为



l

l






l









r

r






r





那么加多少呢?


下面



s

t

,

e

d

,

L

,

R

st,ed,L,R






s


t


,




e


d


,




L


,




R





的定义不同

在一条

树剖链




a

a






a





~



b

b






b





上(意味着



d

f

n

dfn






d


f


n





连续,线段树维护的就是这一段连续的区间)执行从



L

2

L^2







L










2












加到



R

2

R^2







R










2
















  1. a

    a






    a





    ~



    b

    b






    b









    d

    e

    p

    [

    a

    ]

    <

    d

    e

    p

    [

    b

    ]

    dep[a] < dep[b]






    d


    e


    p


    [


    a


    ]




    <








    d


    e


    p


    [


    b


    ]





    的情况

    对于每个



    i

    i






    i





    ,加的值是



    (

    s

    t

    +

    i

    )

    2

    (st+i)^2






    (


    s


    t




    +








    i



    )










    2













    考虑



    a

    a






    a





    ~



    b

    b






    b





    间的某个点



    x

    x






    x





    我们计算的



    i

    =

    d

    f

    n

    [

    x

    ]

    d

    f

    n

    [

    a

    ]

    +

    1

    i = dfn[x] – dfn[a]+1






    i




    =








    d


    f


    n


    [


    x


    ]













    d


    f


    n


    [


    a


    ]




    +








    1





    ,也就是段落第几个数

    起始加的值



    s

    t

    =

    R

    l

    e

    n

    (

    a

    ,

    b

    )

    st = R-len(a,b)






    s


    t




    =








    R













    l


    e


    n


    (


    a


    ,




    b


    )






    带入公式中能够推到



    (

    R

    l

    e

    n

    +

    d

    f

    n

    [

    x

    ]

    d

    f

    n

    [

    a

    ]

    +

    1

    )

    2

    (R-len+dfn[x] – dfn[a] + 1)^2






    (


    R













    l


    e


    n




    +








    d


    f


    n


    [


    x


    ]













    d


    f


    n


    [


    a


    ]




    +








    1



    )










    2













    进一步化为



    (

    d

    f

    n

    [

    x

    ]

    (

    d

    f

    n

    [

    a

    ]

    (

    R

    l

    e

    n

    +

    1

    )

    )

    )

    2

    (dfn[x] – (dfn[a] – (R-len+1)))^2






    (


    d


    f


    n


    [


    x


    ]













    (


    d


    f


    n


    [


    a


    ]













    (


    R













    l


    e


    n




    +








    1


    )


    )



    )










    2













    上面为

    什么要

    化成这种形式呢?因为后面计算用到的是



    a

    n

    s

    =

    s

    1

    d

    f

    n

    [

    x

    ]

    2

    +

    s

    2

    2

    d

    f

    n

    [

    x

    ]

    s

    3

    ans = s1*dfn[x]^2 + s2 – 2*dfn[x]*s3






    a


    n


    s




    =








    s


    1













    d


    f


    n


    [


    x



    ]










    2











    +








    s


    2













    2













    d


    f


    n


    [


    x


    ]













    s


    3





    我们得在区间维护一个针对



    d

    f

    n

    [

    x

    ]

    dfn[x]






    d


    f


    n


    [


    x


    ]





    的二次函数,



    (

    x

    b

    )

    2

    (x-b)^2






    (


    x













    b



    )










    2












    形式,这里可以和上面线段树的知识对应起来





  2. b

    b






    b





    ~



    a

    a






    a









    d

    e

    p

    [

    a

    ]

    <

    d

    e

    p

    [

    c

    ]

    dep[a] < dep[c]






    d


    e


    p


    [


    a


    ]




    <








    d


    e


    p


    [


    c


    ]





    的情况(



    a

    a






    a





    ~



    b

    b






    b









    d

    f

    n

    dfn






    d


    f


    n





    连续增)

    对于每个i,加的值是



    (

    e

    d

    i

    )

    2

    (ed-i)^2






    (


    e


    d













    i



    )










    2













    考虑



    a

    a






    a





    ~



    b

    b






    b





    的某个点x,对应的



    i

    =

    d

    f

    n

    [

    x

    ]

    d

    f

    n

    [

    a

    ]

    +

    1

    i = dfn[x] – dfn[a] + 1






    i




    =








    d


    f


    n


    [


    x


    ]













    d


    f


    n


    [


    a


    ]




    +








    1





    ,从



    a

    a






    a









    b

    b






    b





    第几个数




    b

    b






    b





    ~



    a

    a






    a





    最后一个加的值



    e

    d

    =

    L

    +

    l

    e

    n

    (

    a

    ,

    b

    )

    ed = L + len(a, b)






    e


    d




    =








    L




    +








    l


    e


    n


    (


    a


    ,




    b


    )






    带入公式种能够推到



    (

    e

    d

    +

    d

    f

    n

    [

    a

    ]

    d

    f

    n

    [

    x

    ]

    1

    )

    2

    (ed + dfn[a] – dfn[x] – 1)^2






    (


    e


    d




    +








    d


    f


    n


    [


    a


    ]













    d


    f


    n


    [


    x


    ]













    1



    )










    2













    进一步化为



    (

    (

    d

    f

    n

    [

    a

    ]

    +

    (

    L

    +

    l

    e

    n

    1

    )

    )

    d

    f

    n

    [

    x

    ]

    )

    2

    ((dfn[a]+(L+len-1))-dfn[x])^2






    (


    (


    d


    f


    n


    [


    a


    ]




    +








    (


    L




    +








    l


    e


    n













    1


    )


    )













    d


    f


    n


    [


    x


    ]



    )










    2













    也会等于



    (

    d

    f

    n

    [

    x

    ]

    (

    d

    f

    n

    [

    a

    ]

    +

    (

    L

    +

    l

    e

    n

    +

    1

    )

    )

    )

    2

    (dfn[x]-(dfn[a]+(L+len+1)))^2






    (


    d


    f


    n


    [


    x


    ]













    (


    d


    f


    n


    [


    a


    ]




    +








    (


    L




    +








    l


    e


    n




    +








    1


    )


    )



    )










    2












综上

通过维护形如



(

x

b

)

2

(x-b)^2






(


x













b



)










2












的二次函数,也就是维护其中关于的三个系数,就能维护正的加平方和倒着加平方的值

树链剖分,对于每一个链



(

t

o

p

[

a

]

,

a

)

(top[a],a)






(


t


o


p


[


a


]


,




a


)





,如果是从



x

x






x





加到



y

y






y





方向的,则按第一种方法正的加,起始加的值



s

t

st






s


t





是每次加完跟新后的值,区间长度



l

e

n

=

d

e

p

[

a

]

d

e

p

[

t

o

p

[

a

]

]

+

1

len = dep[a] – dep[top[a]]+1






l


e


n




=








d


e


p


[


a


]













d


e


p


[


t


o


p


[


a


]


]




+








1





,如果是反方向就第二种加法,取法相同不多赘述



下面详细代码

//6962 
/*
  @Author: YooQ
*/
#include <bits/stdc++.h>
using namespace std;
#define sc scanf
#define pr printf
#define ll long long
#define int long long
#define FILE_OUT freopen("out", "w", stdout);
#define FILE_IN freopen("in", "r", stdin);
#define debug(x) cout << #x << ": " << x << "\n";
#define AC 0
#define WA 1
#define INF 0x3f3f3f3f
const ll MAX_N = 2e6+5;
const ll MOD = 1e9+7;
int N, M, K;

struct Seg{
	struct Tr{
		int k;
		int lazy;
	}tr[MAX_N];
	
	void push_up(int rt) {
		tr[rt].k = tr[rt<<1].k + tr[rt<<1|1].k;
	}
	
	void calc(int rt, int len, int add) {
		tr[rt].k += len * add;
		tr[rt].lazy += add;
	}
	
	void push_down(int rt, int l, int r) {
		int lc = rt<<1;
		int rc = rt<<1|1;
		int mid = l + ((r-l)>>1);
		calc(lc, mid - l + 1, tr[rt].lazy);
		calc(rc, r - mid, tr[rt].lazy);
		tr[rt].lazy = 0;
	}
	
	void build(int rt, int l, int r) {
		tr[rt].k = 0;
		tr[rt].lazy = 0;
		if (l == r) return;
		int mid = l + ((r-l)>>1);
		build(rt<<1, l, mid);
		build(rt<<1|1, mid+1, r);
		push_up(rt);
	}
	
	void update(int rt, int l, int r, int x, int y, int k) {
		if (x <= l && r <= y) {
			calc(rt, r-l+1, k);
			return;
		}
		push_down(rt, l, r);
		int mid = l + ((r-l)>>1);
		if (x <= mid) update(rt<<1, l, mid, x, y, k);
		if (y >  mid) update(rt<<1|1, mid+1, r, x, y, k);
		push_up(rt);
	}
	
	int query(int rt, int l, int r, int x) {
		if (l == r) {
			return tr[rt].k;
		}
		push_down(rt, l, r);
		int mid = l + ((r-l)>>1);
		if (x <= mid) return query(rt<<1, l, mid, x);
		if (x >  mid) return query(rt<<1|1, mid+1, r, x);
	}
}s1, s2, s3;

int head[MAX_N];
int tot = 0;
struct Edge{
	int to, nxt;
}edge[MAX_N];

void addEdge(int u, int v) {
	edge[tot].nxt = head[u];
	edge[tot].to = v;
	head[u] = tot++;
	edge[tot].nxt = head[v];
	edge[tot].to = u;
	head[v] = tot++;
}

int rk[MAX_N];
int indx = 0;
int dfn[MAX_N];
int son[MAX_N];
int sz[MAX_N];
int father[MAX_N];
int dep[MAX_N];
int top[MAX_N];

void dfs1(int u, int from, int d) {
	dep[u] = d;
	father[u] = from;
	sz[u] = 1;
	son[u] = 0;
	int v;
	for (int i = head[u];~i;i=edge[i].nxt) {
		if ((v=edge[i].to) == from) continue;
		dfs1(v, u, d+1);
		sz[u] += sz[v];
		if (sz[v] > sz[son[u]]) {
			son[u] = v;
		}
	}
}

void dfs2(int u, int tp) {
	top[u] = tp;
	dfn[u] = ++indx;
	rk[indx] = u;
	if (!son[u]) return;
	dfs2(son[u], tp);
	int v;
	for (int i = head[u];~i;i=edge[i].nxt) {
		if ((v=edge[i].to) == son[u] || v == father[u]) continue;
		dfs2(v, v);
	}
}

int LCA(int x, int y) {
	while (top[x] != top[y]) {
		if (dep[top[x]] >= dep[top[y]]) {
			x = father[top[x]];
		} else {
			y = father[top[y]];
		}
	}
	return dep[x] < dep[y] ? x : y;
}


void change(int x, int y) {
	int lca = LCA(x, y);
	int l = 1, r = dep[x] + dep[y] - 2 * dep[lca] + 1;
	int add = 0, len = 0;
	while (top[x] != top[y]) {
		if (dep[top[x]] >= dep[top[y]]) {
			len =  dep[x] - dep[top[x]] + 1;
			l += len;
			add = dfn[top[x]] + (l - 1);
			s1.update(1, 1, N, dfn[top[x]], dfn[x], 1);
			s2.update(1, 1, N, dfn[top[x]], dfn[x], add * add);
			s3.update(1, 1, N, dfn[top[x]], dfn[x], add);
			x = father[top[x]];
		} else {
			len = dep[y] - dep[top[y]] + 1;
			r -= len;
			add = dfn[top[y]] - (r + 1);
			s1.update(1, 1, N, dfn[top[y]], dfn[y], 1);
			s2.update(1, 1, N, dfn[top[y]], dfn[y], add * add);
			s3.update(1, 1, N, dfn[top[y]], dfn[y], add);
			y = father[top[y]];
		}
	}
	if (dep[x] > dep[y]) {
		len = dep[x] - dep[y] + 1;
		l += len;
		add = dfn[y] + (l - 1);
		s1.update(1, 1, N, dfn[y], dfn[x], 1);
		s2.update(1, 1, N, dfn[y], dfn[x], add * add);
		s3.update(1, 1, N, dfn[y], dfn[x], add);
	} else {
		len = dep[y] - dep[x] + 1;
		r -= len;
		add = dfn[x] - (r + 1);
		s1.update(1, 1, N, dfn[x], dfn[y], 1);
		s2.update(1, 1, N, dfn[x], dfn[y], add * add);
		s3.update(1, 1, N, dfn[x], dfn[y], add);
	}
}

void init() {
	memset(head, -1, sizeof head);
	tot = 0;
	indx = 0;
}

void solve(){
	init();
	sc("%lld", &N);
	int u, v, w;
	for (int i = 1; i < N; ++i) {
		sc("%lld%lld", &u, &v);
		addEdge(u, v);
	}
	dfs1(1, 0, 1);dfs2(1, 1);
	sc("%lld", &M);
	int opt, a, b, x;
	s1.build(1, 1, N);s2.build(1, 1, N);s3.build(1, 1, N);
	for (int i = 1; i <= M; ++i) {
		sc("%lld", &opt);
		if (opt == 1) {
			sc("%lld%lld", &a, &b);
			change(a, b);
		} else {
			sc("%lld", &x);
			int ans = s1.query(1, 1, N, dfn[x]) * dfn[x] * dfn[x] + s2.query(1, 1, N, dfn[x]) - 2 * dfn[x] * s3.query(1, 1, N, dfn[x]);
			pr("%lld\n", ans);
		}
	}
}

signed main()
{
	#ifndef ONLINE_JUDGE
//	FILE_IN
	FILE_OUT
	#endif
	int T = 1;//cin >> T;
	while (T--) solve();

	return AC;
}





版权声明:本文为qq_45754027原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。