zoukankan      html  css  js  c++  java
  • 【GDSOI2019】滑稽二乘法【数据结构】【LCT】

    Description

    在这里插入图片描述
    点数<=100000,操作数<=200000

    Solution

    经典的LCT维护子树路径信息的问题。

    具体来说,我们对于每一个节点,它在splay上的子树对应了原树中的一条祖先后代链(换过根的),记录这个点的splay子树中的所有黑点以及它们的虚子树中的所有黑点分别到这条祖先后代链的链顶和链底的0次,1次,2次距离和,另外记录splay的子树的所有虚儿子到这条链的答案。

    update相当于是合并两条链,以到链顶为例,左半边直接加,右半边每个的距离要多左子树的大小,利用((x+v)^2=x^2+2xv+v^2)拆开来计算即可。

    同时维护到链顶和链底是为了在reverse操作的时候能够快速求出答案。

    update以及access的时候0次1次2次项拆系数合并即可,可以写成模块化就简单一些。

    时间复杂度(O(nlog n))

    Code

    我也不知道为什么不开O2跑不过...

    #pragma GCC optimize(2)
    #include <bits/stdc++.h>
    #define fo(i,a,b) for(int i=a;i<=b;++i)
    #define fod(i,a,b) for(int i=a;i>=b;--i)
    typedef long long LL;
    const int N=100005;
    const int mo=998244353;
    using namespace std;
    int n;
    LL sqr(const LL x) {return x*x;}
    namespace LCT
    {
    	int fn[N],f[N],t[N][2],r[N];
    	LL sz[N],vl[N][3],sp[N],sm[N][2][3];
    	LL cl[N];
    	void rev(const int k)
    	{
    		r[k]^=1,swap(sm[k][0],sm[k][1]);
    	}
    	void down(int k)
    	{
    		if(r[k]) 
    		{
    			swap(t[k][0],t[k][1]),fn[t[k][0]]=0,fn[t[k][1]]=1;
    			rev(t[k][0]),rev(t[k][1]);
    			r[k]=0;
    		}	
    	}
    	void merge(LL *a,LL *b,const LL u,const LL v)
    	{
    		a[0]=a[0]+v*b[0];
    		a[1]=a[1]+v*(b[1]+b[0]*u);
    		a[2]=a[2]+v*(b[2]+b[1]*u*(LL)2+b[0]*sqr(u));
    	}
    	void up(int k)
    	{
    		if(!k) return;
    		sz[k]=sz[t[k][0]]+sz[t[k][1]]+1;
    		sp[k]=sp[t[k][0]]+vl[k][2]+sp[t[k][1]];
    		fo(x,0,1)
    		{	
    			int u=t[k][x],v=t[k][1^x];
    			sm[k][x][0]=cl[k],sm[k][x][1]=sz[u]*cl[k],sm[k][x][2]=sqr(sz[u])*cl[k];
    			merge(sm[k][x],sm[u][x],0,1);
    			merge(sm[k][x],vl[k],sz[u],1);
    			merge(sm[k][x],sm[v][x],sz[u]+1,1);
    		}
    	}
    	void hb(int x,int y,int p)
    	{
    		if(x&&p>=0) t[x][p]=y;
    		if(y) fn[y]=p,f[y]=x;
    	}
    	void rot(int k)
    	{
    		int fa=f[k],p=fn[k];
    		hb(fa,t[k][1-p],p);
    		hb(f[fa],k,fn[fa]);
    		hb(k,fa,1-p);
    		up(fa),up(k),up(f[k]);
    	}
    	int d[N];
    	void splay(int k,int x)
    	{
    		d[d[0]=1]=k;
    		while(fn[d[d[0]]]!=-1&&f[d[d[0]]]!=x) d[++d[0]]=f[d[d[0]-1]];
    		fod(i,d[0],1) down(d[i]);
    		while(f[k]!=x&&fn[k]!=-1) 
    		{
    			if(fn[f[k]]==-1||f[f[k]]==x) rot(k);
    			else if(fn[k]==fn[f[k]]) rot(f[k]),rot(k);
    			else rot(k),rot(k);
    		}
    	}
    	void access(int k)
    	{
    		int r=k;
    		splay(k,0);
    		merge(vl[k],sm[t[k][1]][0],1,1),hb(k,t[k][1],-1),t[k][1]=0;
    		up(k);
    		while(f[k])
    		{
    			int x=f[k];splay(x,0);
    			merge(vl[x],sm[t[x][1]][0],1,1);
    			merge(vl[x],sm[k][0],1,-1);
    			fn[t[x][1]]=-1,hb(x,k,1),up(x),k=x;
    		}
    		splay(r,0);
    	}
    	void make(int k)
    	{
    		access(k),rev(k);
    	}
    	void link(int x,int y)
    	{
    		make(y),access(x);
    		f[y]=x,fn[y]=-1;
    		up(x);
    	}
    	void init()
    	{
    		fo(i,1,n) sz[i]=1;
    		fo(i,1,n-1)
    		{
    			int x,y;
    			scanf("%d%d",&x,&y);
    			link(x,y);
    		}
    	}
    	void modify(int k)
    	{
    		make(k),cl[k]^=1,up(k);
    	}
    	LL query(int x,int y)
    	{
    		make(x),access(y);
    		return sp[y];
    	}
    }
    using namespace LCT;
    int main()
    {
    	cin>>n;
    	init();
    	int q;
    	cin>>q;
    	fo(i,1,q)
    	{	
    		int tp,x,y;
    		scanf("%d%d",&tp,&x);
    		if(tp==0) modify(x);
    		else 
    		{
    			scanf("%d",&y);
    			printf("%lld
    ",query(x,y)%mo);
    		}
    	}
    }
    
  • 相关阅读:
    ASP.NET中的ViewState
    (标记)Spring.Net+NHibenate+Asp.Net mvc +ExtJs 系列 By 似水流年
    C#中类的定义
    苹果CMS搭建影视网站教程
    Java之冒泡排序
    Java之数组扩容
    Linux之netstat命令基本使用
    Linux之systemctl命令基本使用
    Oracle11g R2 安装教程(非常详细 )
    Linux之firewall防火墙开启和关闭
  • 原文地址:https://www.cnblogs.com/BAJimH/p/11093684.html
Copyright © 2011-2022 走看看