zoukankan      html  css  js  c++  java
  • HDU 5405 Sometimes Naive(动态树)

    题意

    (n) 个节点的树,每个点有点权,(m) 次操作,操作分两种,修改一个节点的点权,对于一个 ((u,v)) ,询问 (displaystylesum_{i=1}^nsum_{j=1}^n f(i,j)) 的值,其中如果路径 ((i,j)) 与路径 ((u,v)) 有公共点,(f(i,j)=w_iw_j)(w_i) 表示节点 (i) 的点权),否则 (f(i,j)=0)

    (1leq n,m leq 10^5​)

    思路

    先讲一下用 ( ext{LCT}​) 维护子树信息的写法(以子树和为例)。

    我们需要一个两个数组维护和,(sum,isum) 前者表示子树和,后者表示虚儿子的和。

    ( ext{push_up}) 函数需要这么写

    void push_up(int x){sum[x]=sum[ch[x][0]]+sum[ch[x][1]]+isum[x]+pw[x];}
    

    在连断虚实边时,要注意 (isum) 的变化。

    void access(int x)
    {
    	for(int y=0;x;y=x,x=fa[x])
    		splay(x),isum[x]+=-sum[y]+sum[ch[x][1]],ch[x][1]=y,push_up(x);
    	//其实这里可以不用push_up(x),因为sum值没有变化
    }
    

    ( ext{link}) 函数这样写,因为需要维护子树和,所以两个点直接转到根。

    void link(int x,int y)
    {
    	make_root(x),make_root(y);
    	fa[x]=y;
    	isum[y]+=sum[x];
    	push_up(y);
    }
    

    ( ext{cut}​) 函数同理

    void cut(int x,int y)
    {
    	make_root(x),access(y),splay(x);
    	ch[x][1]=fa[y]=0;
    	push_up(x);
    }
    

    正难则反,我们用总的路径 ((i,j)) 的答案减去没有公共点的路径 ((i,j)) 的答案。

    总的路径非常好求,就是 (displaystylesum_{i=1}^ndisplaystylesum_{j=1}^nw_iw_j= (sum_{i=1}^n w_i)^2​)

    为了求没有交点的路径,我们不妨利用 ( ext{LCT}) 将这条路径的一个端点拎到根,那对与询问 ((u,v)) ,答案就是这个式子:

    [(sum_{i=1}^n w_i)^2-sum_{pin ext{Path}(u,v)} sum_{qin ext{son}(p) ext{且}q otin{ ext{Path}(u,v)}}sum_q^2 ]

    其中 (sum_i) 表示子树和,我们需要用上面的方法去维护它。不难发现,实化路径 ((u,v)) 后,上面的 (q) 其实就是虚儿子的 (sum) 平方再求和,那用类似的方法进行维护。然后再对这个东西再求一次路径和即可。最终用总的去减就是最终答案了。

    代码

    #include<bits/stdc++.h>
    #define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
    #define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
    template<typename T,typename _T>inline bool chk_min(T &x,const _T y){return y<x?x=y,1:0;}
    template<typename T,typename _T>inline bool chk_max(T &x,const _T y){return x<y?x=y,1:0;}
    typedef long long ll;
    const int N=1e5+5;
    const int P=1e9+7;
    int ch[N][2],fa[N];bool rev[N];
    int stk[N],tp;
    int pw[N],sum[N],isum[N],Sum[N],ans[N];
    int n,m;
    
    void create(int x,int val)
    {
    	ch[x][0]=ch[x][1]=fa[x]=rev[x]=0;
    	pw[x]=sum[x]=val;
    	isum[x]=Sum[x]=ans[x]=0;
    }
    bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
    void reved(int x)
    {
    	std::swap(ch[x][0],ch[x][1]);
    	rev[x]^=1;
    }
    void push_up(int x)
    {
    	sum[x]=((ll)sum[ch[x][0]]+sum[ch[x][1]]+pw[x]+isum[x])%P;
    	ans[x]=((ll)ans[ch[x][0]]+ans[ch[x][1]]+Sum[x])%P;
    }
    void push_down(int x)
    {
    	if(rev[x])
    	{
    		if(ch[x][0])reved(ch[x][0]);
    		if(ch[x][1])reved(ch[x][1]);
    		rev[x]=0;
    	}
    }
    void rotate(int x)
    {
    	int y=fa[x],z=fa[y],k=(x==ch[y][1]);
    	if(!isroot(y))ch[z][y==ch[z][1]]=x; fa[x]=z;
    	ch[y][k]=ch[x][!k]; if(ch[x][!k])fa[ch[x][!k]]=y;
    	ch[x][!k]=y,fa[y]=x;
    	push_up(y),push_up(x);
    }
    void splay(int x)
    {
    	stk[tp=1]=x;
    	for(int y=x;!isroot(y);y=fa[y])stk[++tp]=fa[y];
    	while(stk[tp])push_down(stk[tp]),tp--;
    	while(!isroot(x))
    	{
    		int y=fa[x],z=fa[y];
    		if(!isroot(y))(x==ch[y][1])==(y==ch[z][1])?rotate(y):rotate(x);
    		rotate(x);
    	}
    }
    void access(int x)
    {
    	for(int y=0;x;y=x,x=fa[x])
    	{
    		splay(x);
    		(isum[x]+=(-(ll)sum[y]+sum[ch[x][1]])%P)%=P;
    		(Sum[x]+=(-(ll)sum[y]*sum[y]%P+(ll)sum[ch[x][1]]*sum[ch[x][1]])%P)%=P;
    		ch[x][1]=y;
    		push_up(x);
    	}
    }
    void make_root(int x)
    {
    	access(x),splay(x),reved(x);
    }
    int get_root(int x)
    {
    	access(x),splay(x);
    	while(ch[x][0])push_down(x),x=ch[x][0];
    	splay(x);
    	return x;
    }
    void link(int x,int y)
    {
    	make_root(x),make_root(y);
    	fa[x]=y;
    	(isum[y]+=sum[x])%=P;
    	(Sum[y]+=(ll)sum[x]*sum[x]%P)%=P;
    	push_up(y);
    }
    void lift(int x,int y)
    {
    	make_root(x),access(y),splay(x);
    }
    void update(int x,int val)
    {
    	lift(x,x);
    	pw[x]=val;
    	push_up(x);
    }
    int query(int x,int y)
    {
    	lift(x,y);
    	return (((ll)sum[x]*sum[x]%P-ans[x])%P+P)%P;
    }
    
    int main()
    {
    	while(~scanf("%d%d",&n,&m))
    	{
    		FOR(i,1,n)
    		{
    			int x;
    			scanf("%d",&x);
    			create(i,x);
    		}
    		FOR(i,1,n-1)
    		{
    			int u,v;
    			scanf("%d%d",&u,&v);
    			link(u,v);
    		}
    		while(m--)
    		{
    			int op,u,v;
    			scanf("%d%d%d",&op,&u,&v);
    			if(op==1)update(u,v);
    			else if(op==2)printf("%d
    ",query(u,v));
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    使用JQuery从客户端调用C#方法
    上传文件插件 Uploadify使用说明 转
    juqery 操作select
    XP下安装IIS6.0的办法 转
    更改2003远程桌面端口3389为其他端口号
    Web打印
    远程桌面 客户端无法建立跟远程计算机的连接 解决办法
    WPF的“.NET研究”消息机制(一) 让应用程序动起来 狼人:
    应用Visual Studio 2010辅“.NET研究”助敏捷测试(上) 狼人:
    ASP.NET调用.sql文件(二“.NET研究”) 狼人:
  • 原文地址:https://www.cnblogs.com/Paulliant/p/10557332.html
Copyright © 2011-2022 走看看