• P3920 【[WC2014]紫荆花之恋】


    本人第2题点分树,就搞这题大毒瘤,写了十来个小时吧,几个sb错误捂脸

    其实点分树普遍的叫法是动态点分治,但是我还是喜欢叫点分树,原因结尾说

    翻了一遍题解,我的代码量是最小的,而且代码还算可读。(纯数组)

    人傻常数大,不保证任何时候都跑的过去(写题解的忽然发现是多开了2个longlong......)

    你谷的评测波动太恐怖了,我的#8跑2次,一次12.01s,一次6.8s,差了快2倍了


    • 总思路

    这题要求的是:

    (dis_{u,v}le r_u+r_v)

    (dis_{u,lca}+dis_{v,lca}le r_u+r_v)

    (r_u-dis_{u,lca}ge dis_{v,lca}-r_v)

    如果已经建出了分治树,那么这个可以加上平衡树容斥出来(平衡树维护排名)

    虽然直接加叶子不会对重心影响很大,但是加多了就可能被卡,所以考虑类似替罪羊的重构分治树


    本篇题解变量声明:

    vt,to 分治树上的父亲,父亲指向的子节点(to是个vector)

    fa,dep,dist 倍增求2点间距离用,fa是倍增数组,dep是节点深度,dist是距离根的距离

    val 题目中的r,感知能力

    mx,siz,root,totsize,used ,求子树重心用,mx是最大子树大小,siz是当前子树大小,root是重心,totsize是要求重心的子树的总大小,是否使用过的标记

    rub,st,rub_top,st_top ,rub是垃圾箱,st是重构树的时候存节点编号的

    num_node 平衡树总结点数

    key,sz,ch 平衡树的权值,子树大小,左右儿子

    t1,t2 容斥用的2个根节点


    • 平衡树

    据说卡常数大的平衡树,于是不敢写我最常写的fhqtreap,写了替罪羊。但是之前没写过替罪羊,纯手推。

    说说手推的过程吧:

    知道了替罪羊是BST+重构保证复杂度,所以考虑写个BST,插入结束以后判断是否需要重构即可。重构的话就遍历整颗子树,把所有编号存起来,注意中序遍历,因为中序遍历的时候就是有序的,否则重构多个排序,多个log。还需要垃圾回收,和重构的写法差不多吧qwq。但是要么从垃圾箱里拿出来的时候清零元素,要么遍历完立即清零元素,不然会把整颗树变成图的(左右儿子没清零的后果)

    然后是空间,我第一次写的时候想不通空间怎么开,过了一天忽然会了:我们插节点的时候是因为它有分治树上的父亲,而这个是log级别的,每个节点一个log,加个垃圾回收空间严格 (nlog n)

    但是我们对每个节点开2颗,容斥用,所以空间是 (2 imes 10^5 imes log (10^5)) ,开个4000000比较保险(


    • 统计答案

    初学点分树最难理解的地方吧(反正我理解了好久,菜死了)

    容斥:对于每个节点x维护2颗平衡树(t1[x],t2[x]),分别维护(vt是x沿分治树到根所有节点) (dis_{vt,x}-val_x)(dis_{vt(vt),x})

    注意平衡树插入和查询的时候传的值需要反号,原因见总思路的推导

    我们发现,如果只维护一棵平衡树,在跳到上一层分治中心的时候,从当前节点到分治中心的路径会被多算一遍,所以要容斥掉。所以再维护一棵就好了。


    • 重构点分树

      把整个子树拍扁,然后找到重心,当做根,注意维护父子关系,剩下直接普通的建点分树即可

      但是在重构的时候还要重构对应节点的平衡树。这时候可以直接推进垃圾箱,然后像上面一样维护2颗平衡树,重构结束。


    • 插入节点

    可以直接往查到点分树上的叶子节点,如果太多不平衡了直接重建。所以一开始完全珂以把它的父亲当做上一层分治中心,等不平衡了就是重构的事了。


    • 注意事项:

      1. 用高速平衡树

      2. 注意重构替罪羊的时候清空元素(我跳过的坑)

      3. 统计答案的时候记得开 long long ,因为最大是 (10^5 imes10^5),其余别开,不然不稳(我跳过的坑)

      4. 如果下数据本地跑,记得开大栈空间,不然会因为重构时dfs的太深RE(我跳过的坑)


    代码,还是放出来吧,说不定可以帮一些人。

    #include<bits/stdc++.h>
    using namespace std;
    #define rint register int
    typedef long long LL;
    inline int rd(){
       int x=0,f=1;
       char ch=getchar();
       while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
       while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();
       return x*f;
    }
    inline int min(const int &a,const int &b) {return a<b?a:b;}
    inline int max(const int &a,const int &b) {return a>b?a:b;}
    const int N=100010;
    const int M=4000010;
    const int mod=1000000000;
    const double alpha=0.8;
    const double beta=0.7;
    int n,val[N];
    LL ans;
    int head[N],num_edge;
    int fa[18][N],bin[18],dep[N],dist[N];
    int mx[N],siz[N],root,vt[N],totsize;
    bool used[N];
    int rub[M],rub_top,st[M],st_top,num_node;
    int sz[M],ch[M][2],key[M];
    int t1[N],t2[N];
    vector<int>to[N];
    struct egde {
    	int to,nxt;
    }e[N<<1];
    void addedge(int from,int to) {
    	++num_edge;
    	e[num_edge].nxt=head[from];
    	e[num_edge].to=to;
    	head[from]=num_edge;
    }
    int lca(int x,int y) {
    	if(dep[x]<dep[y])x^=y^=x^=y;
    	int delta=dep[x]-dep[y];
    	for(rint i=17;i>=0;--i)if(delta&bin[i])x=fa[i][x];
    	if(x==y)return x;
    	for(rint i=17;i>=0;--i)if(fa[i][x]!=fa[i][y])x=fa[i][x],y=fa[i][y];
    	return fa[0][x];
    }
    LL dis(int x,int y) {return dist[x]+dist[y]-(dist[lca(x,y)]<<1);}
    void push_in_rub(int u) {
    	if(!u)return;
    	push_in_rub(ch[u][0]);
    	rub[++rub_top]=u;
    	push_in_rub(ch[u][1]);
    	ch[u][0]=ch[u][1]=sz[u]=key[u]=0;
    }
    void pushup(int u) {
    	sz[u]=sz[ch[u][0]]+sz[ch[u][1]]+1;
    }
    void push_in_st(int u) {
    	if(!u)return;
    	push_in_st(ch[u][0]);
    	st[++st_top]=u;
    	push_in_st(ch[u][1]);
    	ch[u][0]=ch[u][1]=0;
    }
    int getnew() {
    	return rub_top?rub[rub_top--]:++num_node;
    }
    void build(int &x,int l,int r) {
    	if(l>r)return;
    	int mid=(l+r)>>1;
    	x=st[mid];sz[x]=r-l+1;
    	build(ch[x][0],l,mid-1);
    	build(ch[x][1],mid+1,r);
    }
    void rebuild(int &u) {
    	st_top=0;push_in_st(u);
    	build(u,1,st_top);
    }
    void insert(int &u,int val) {
    	if(!u) {u=getnew(),key[u]=val,sz[u]=1;return;}
    	if(val<=key[u])insert(ch[u][0],val);
    	else insert(ch[u][1],val);
    	pushup(u);
    	if(sz[ch[u][0]]>1.0*sz[u]*beta)rebuild(u);
    	if(sz[ch[u][1]]>1.0*sz[u]*beta)rebuild(u);
    }
    int rk(int u,int val) {
    	int res=0,now=u;
    	while(now) {
    		if(key[now]<val)res+=sz[ch[now][0]]+1,now=ch[now][1];
    		else now=ch[now][0];
    	}
    	return res;
    }
    void getroot(int u,int ft) {
    	mx[u]=0,siz[u]=1;
    	for(rint i=head[u];i;i=e[i].nxt) {
    		int v=e[i].to;
    		if(v==ft||used[v])continue;
    		getroot(v,u);
    		siz[u]+=siz[v];
    		mx[u]=max(mx[u],siz[v]);
    	}
    	mx[u]=max(mx[u],totsize-siz[u]);
    	if(mx[root]>mx[u])root=u;
    }
    void calc(int u,int ft,int Vt) {
    	insert(t1[Vt],dis(u,Vt)-val[u]);
    	if(vt[Vt])insert(t2[Vt],dis(u,vt[Vt])-val[u]);
    	for(rint i=head[u];i;i=e[i].nxt) {
    		int v=e[i].to;
    		if(v==ft||used[v])continue;
    		calc(v,u,Vt);
    	}
    }
    void dfs_build(int u) {
    	calc(u,0,u);used[u]=1;
    	for(rint i=head[u];i;i=e[i].nxt) {
    		int v=e[i].to;
    		if(used[v])continue;
    		totsize=siz[v];
    		root=0;
    		getroot(v,0);
    		vt[root]=u;
    		to[u].push_back(root);
    		dfs_build(root);
    	}
    }
    void dfs_pia(int u) {
    	++totsize;used[u]=0;
    	push_in_rub(t1[u]);
    	push_in_rub(t2[u]);
    	t1[u]=t2[u]=0;
    	for(rint i=0;i<to[u].size();++i)
    		dfs_pia(to[u][i]);
    	to[u].clear();
    }
    void dfs_rebuild(int u) {
    	totsize=0;dfs_pia(u);root=0;getroot(u,0);
    	if(vt[u])
    		for(rint i=0;i<to[vt[u]].size();++i)
    			if(to[vt[u]][i]==u)to[vt[u]][i]=root;
    	vt[root]=vt[u];dfs_build(root);
    }
    void dfs_insert(int x) {
    	for(rint i=x;vt[i];i=vt[i]) {
    		int tmp=val[x]-dis(x,vt[i])+1;
    		ans+=rk(t1[vt[i]],tmp)-rk(t2[i],tmp);
    	}
    	for(rint i=x;i;i=vt[i]) {
    		insert(t1[i],dis(i,x)-val[x]);
    		if(vt[i])insert(t2[i],dis(x,vt[i])-val[x]);
    	}
    	int res=0;
    	for(rint i=x;vt[i];i=vt[i])
    		if(sz[t1[i]]>1.0*sz[t1[vt[i]]]*alpha)res=vt[i];
    	if(res)dfs_rebuild(res);
    }
    signed main() {
    	rd(),n=rd();
    	bin[0]=1;for(rint i=1;i<=17;++i)bin[i]=bin[i-1]<<1;
    	mx[root=0]=n;
    	for(rint i=1;i<=n;++i) {
    		vt[i]=fa[0][i]=(rd()^(ans%mod));
    		int c=rd();val[i]=rd();used[i]=1;
    		dep[i]=dep[fa[0][i]]+1;
    		dist[i]=dist[fa[0][i]]+c;
    		for(rint j=1;j<=17;++j)fa[j][i]=fa[j-1][fa[j-1][i]];
    		if(vt[i])to[vt[i]].push_back(i),addedge(i,fa[0][i]),addedge(fa[0][i],i);
    		dfs_insert(i);printf("%lld
    ",ans);
    	}
    	return 0;
    } 
    

    之前说我喜欢叫点分树,因为如果您和一个刚学不久的OIer讨论,很可能出现如下坑人的对话

    A:您会dfs吗?

    B:会啊,刚学

    A:您学了几个月就会点分树了,吊打我%%%

    B:?

    其实一点都不好玩

    打代码去吧qwq,希望我这篇题解对大家有帮助,希望大家早点AC了这题!

    路漫漫其修远兮,吾将上下而求索
  • 相关阅读:
    POJ 3140 Contestants Division (树形DP,简单)
    POJ 2378 Tree Cutting (树的重心,微变形)
    js数据类型--对象&数组
    关于JS call apply 对象、对象实例、prototype、Constructor、__proto__
    JS模块化编程(四)--require应用
    JS模块化编程(三)
    利用VS正则替换删除文本行首数据等字符
    JS模块化编程(二)
    JS模块化编程(一)
    js继承
  • 原文地址:https://www.cnblogs.com/zzctommy/p/12918520.html
Copyright © 2020-2023  润新知