【BZOJ】2631: tree LCT

【题意】给定n个点的树,每个点初始权值为1,m次操作:1.x到y的点加值,2.断一条边并连一条边,保证仍是树,3.x到y的点乘值,4.x到y的点权值和取模。n,m<=10^5。

【算法】Link-Cut Tree

【题解】区间加和区间乘标记的处理:【BZOJ】1798: [Ahoi2009]Seq 维护序列seq 线段树

splay上维护要注意:

1.上传时加本身。

2.改值的时候不能影响到0点。

3.所有改变点的儿子的地方都要上传,所有改变点的父亲的地方都要下传。

除了rotate,还有access的时候要上传up。

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define int unsigned int
using namespace std;
int read(){
    int s=0,t=1;char c;
    while(!isdigit(c=getchar()))if(c==‘-‘)t=-1;
    do{s=s*10+c-‘0‘;}while(isdigit(c=getchar()));
    return s*t;
}
const int maxn=100010,MOD=51061;
int n,Q,sum[maxn],A[maxn],B[maxn],g[maxn],a[maxn],sz[maxn],t[maxn][2],f[maxn],N[maxn];
int M(int x){return x>=MOD?x-MOD:x;}
void up(int x){sum[x]=M(M(sum[t[x][0]]+sum[t[x][1]])+a[x]);sz[x]=sz[t[x][0]]+sz[t[x][1]]+1;}
void modify_a(int k,int x){
    if(!k)return;
    a[k]=M(a[k]+x);
    sum[k]=M(sum[k]+sz[k]*x%MOD);
    A[k]=M(A[k]+x);
}//make 0 no influence
void modify_b(int k,int x){
    if(!k)return;
    a[k]=a[k]*x%MOD;
    sum[k]=sum[k]*x%MOD;A[k]=A[k]*x%MOD;B[k]=B[k]*x%MOD;
}
void down(int k){
    if(g[k]){
        swap(t[k][0],t[k][1]);
        if(t[k][0])g[t[k][0]]^=1;
        if(t[k][1])g[t[k][1]]^=1;
        g[k]=0;
    }
    if(B[k]!=1){
        modify_b(t[k][0],B[k]);modify_b(t[k][1],B[k]);
        B[k]=1;
    }
    if(A[k]){
        modify_a(t[k][0],A[k]);modify_a(t[k][1],A[k]);
        A[k]=0;
    }
}
bool isroot(int x){return !x||(t[f[x]][0]!=x&&t[f[x]][1]!=x);}
void rotate(int y){
    int x=f[y];
    int k=y==t[x][0];
    t[x][!k]=t[y][k];f[t[y][k]]=x;
    if(!isroot(x))t[f[x]][x==t[f[x]][1]]=y;f[y]=f[x];f[x]=y;
    t[y][k]=x;
    up(x);up(y);
}
void splay(int x){
    int top=x,tot=1;N[1]=x;
    while(!isroot(top))top=f[top],N[++tot]=top;
    for(int i=tot;i>=1;i--)down(N[i]);
    while(!isroot(x)){
        if(isroot(f[x])){rotate(x);break;}
        int X=x==t[f[x]][1],Y=f[x]==t[f[f[x]]][1];
        if(X^Y)rotate(x),rotate(x);
        else rotate(f[x]),rotate(x);
    }
}
void access(int x){
    int y=0;
    while(x){
        splay(x);
        t[x][1]=y;
        up(x);///
        y=x;x=f[x];
    }
}
void reserve(int x){access(x);splay(x);g[x]^=1;}
void link(int x,int y){reserve(x);f[x]=y;}
void find(int x,int y){reserve(x);access(y);splay(y);}
void cut(int x,int y){find(x,y);t[y][0]=f[x]=0;}

char s[10];
#undef int
int main(){
#define int unsigned int
    n=read();Q=read();
    for(int i=1;i<=n;i++)sum[i]=a[i]=1,sz[i]=1,A[i]=0,B[i]=1;
    for(int i=1;i<n;i++)link(read(),read());
    while(Q--){
        scanf("%s",s);
        int x=read(),y=read();
        if(s[0]==‘+‘){
            int z=read();
            find(x,y);modify_a(y,z);
        }
        if(s[0]==‘-‘){
            int u=read(),v=read();
            cut(x,y);link(u,v);
        }
        if(s[0]==‘*‘){
            int z=read();
            find(x,y);modify_b(y,z);
        }
        if(s[0]==‘/‘){
            find(x,y);printf("%d\n",sum[y]);
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/onioncyc/p/8493890.html

时间: 2024-11-07 12:32:18

【BZOJ】2631: tree LCT的相关文章

【BZOJ】1468: Tree(POJ1741)

[题意]给定带边权树,求两点距离<=k的点对数.n<=40000. [算法]点分治 [题解]对于一个区域,选择其重心x作为根,则划分出来的每棵子树都是子区域,可以证明至多划分log n次(通过vis[]划分区域).每次划分所有点都扫描一次,所以仅遍历的复杂度是O(n log n). 对于本题,将点x的所有子树节点dis处理出来后排序,然后用双指针法易得<=k的点对数. 但是,这样会把来自同一子树的路径也计算进去,需要减去.来自同一子树y的距离<=k的路径的数量等同于子树y内路径的距

【BZOJ】1468: Tree(点分治)

http://www.lydsy.com/JudgeOnline/problem.php?id=1468 分治真是一门高大上的东西... 好神... 树分治最好资料是:qzc的<分治算法在树的路径问题中的应用> 我来说说自己的理解: 点分=找重心+分治 找重心尤为重要,因为这关系到时间复杂度. 对于递归式 $$T(n)=aT(n/b)+O(D(n))$$ 这类递归式,如果能保证每一层都是$O(D(n))$,那么时间复杂度会大大减小.(详见算导第三章和第四章) 对于一棵树,如果我们在找到重心后,

【BZOJ】2002: [Hnoi2010]Bounce 弹飞绵羊(lct)

(BZOJ挂了,还没在BZOJ测,先是在wikioi测过了,,) 囧.在军训时立志要学lct!!!这是一道lct的裸题,只有access操作(10行都没有啊亲...缩行大法的话,我就不说了..)(link操作相当于水过),其实lct很简单..想想都有点小激动...... lct用splay维护的话,一下就写好了..但是我在写lct的时候,发现了一些我原来splay的老问题,我原来也知道了的,就是将null的ch给赋值了,因为在rot操作里没有特判,所以导致了null的孩子被赋值了,导致我的lct

【BZOJ】2049 [Sdoi2008]Cave 洞穴勘测

[算法]Link-Cut Tree [题解]lct 不是很懂你们会压常数的>_<! #include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int maxn=10010; int f[maxn],t[maxn][2],n,m; bool g[maxn]; bool isroot(int x) {return (t[f[x]][0]!=x&&am

【BZOJ】3319: 黑白树

http://www.lydsy.com/JudgeOnline/problem.php?id=3319 题意:给一棵n节点的树(n<=1e6),m个操作(m<=1e6),每次操作有两种:1.查询u到根的第一条黑边的编号.2.将u到v的路径全部染成黑色 #include <cstdio> #include <cstring> #include <cmath> #include <string> #include <iostream>

【BZOJ】4764 弹飞大爷

[算法]Link-Cut Tree [题意]一个n个数字组成的序列,a[i]表示移动到i+a[i]处,序列值可动态修改,求从i处开始移动到序列外的最小步数. [题解]将序列视为n个点,外界视为n+1,则每个点有且只有一条边连出去,由该性质可知是一个基环内向森林,问题转化为支持插入删除边并求点(n+1)到点i的距离. 由基环内向森林以及点n+1不能向外出边可知点n+1必然在无环的树上,所以若出现环则输出-1.

【bzoj】4538: [Hnoi2016]网络

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4538 维护一个数据结构支持对于一颗树的操作,需要支持: 1.对于树上的一条路径上的每个点上放一个值. 2.撤销某次操作的路劲放. 3.查询除了经过这个点的路径的最大值. 往一个路径上丢值相当于往不经过条路径的所有点上丢值. 用一个树链剖分即可维护,对于操作区间取反. 直接查询单点最大值即可. 为了维护单点最大值,线段树中的每一个点对应两个堆,用于维护插入誉删除. 防止爆空间,所以标记永久

【BZOJ】1821: [JSOI2010]Group 部落划分 Group(最小生成树+贪心)

http://www.lydsy.com:808/JudgeOnline/problem.php?id=1821 这题裸题. 本题要求最短距离最长,很明显,我们排序. 这里存在贪心,即我们把边权最小的全分给n个部落的内部,然后剩下的边最小的就是答案. 将边权较小的边分给k个部落,用并查集生成最小树,使得内部的边总是小于连到外部的边.然后分剩下k个点即可,剩下的k个点的那条边一定是部落之间最小的且最长的边. #include <cstdio> #include <cstring> #

【Leetcode】Binary Tree Postorder Traversal

Given a binary tree, return the postorder traversal of its nodes' values. For example: Given binary tree {1,#,2,3}, 1 2 / 3 return [3,2,1]. Note: Recursive solution is trivial, could you do it iteratively? 思路:后序遍历比起先序遍历以及中序遍历要稍微复杂一点,可以考虑用两个stack进行操作,