【刷题】洛谷 P1501 [国家集训队]Tree II

题目描述

一棵n个点的树,每个点的初始权值为1。对于这棵树有q个操作,每个操作为以下四种操作之一:

  • + u v c:将u到v的路径上的点的权值都加上自然数c;
  • - u1 v1 u2 v2:将树中原有的边(u1,v1)删除,加入一条新边(u2,v2),保证操作完之后仍然是一棵树;
  • * u v c:将u到v的路径上的点的权值都乘上自然数c;
  • / u v:询问u到v的路径上的点的权值和,求出答案对于51061的余数。

输入输出格式

输入格式:

第一行两个整数n,q

接下来n-1行每行两个正整数u,v,描述这棵树

接下来q行,每行描述一个操作

输出格式:

对于每个/对应的答案输出一行

输入输出样例

输入样例#1:

3 2
1 2
2 3
* 1 3 4
/ 1 1

输出样例#1:

4

说明

10%的数据保证,1<=n,q<=2000

另外15%的数据保证,1<=n,q<=5*10^4,没有-操作,并且初始树为一条链

另外35%的数据保证,1<=n,q<=5*10^4,没有-操作

100%的数据保证,1<=n,q<=10^5,0<=c<=10^4

By (伍一鸣)

题解

这个题类似模板,只是变成了维护加法和乘法
类似于维护线段数时的加法和乘法
维护乘法时,要乘维护的和,点的权值和加法的lazy标记
维护加法的话,就直接加就行了
(这道题我有个地方少打了个=号,结果找了一个半小时。。)

#include<bits/stdc++.h>
#define ll long long
#define db double
#define ld long double
#define lc(x) ch[(x)][0]
#define rc(x) ch[(x)][1]
const int MAXN=100000+10,Mod=51061;
ll n,q,Val[MAXN];
struct LCT{
    ll ch[MAXN][2],fa[MAXN],rev[MAXN],size[MAXN],sum[MAXN],add[MAXN],mul[MAXN];
    inline void init()
    {
        for(register int i=1;i<=n;++i)Val[i]=size[i]=sum[i]=mul[i]=1;
        memset(ch,0,sizeof(ch));
        memset(fa,0,sizeof(fa));
        memset(rev,0,sizeof(rev));
    }
    inline bool nroot(ll x)
    {
        return rc(fa[x])==x||lc(fa[x])==x;
    }
    inline void reverse(ll x)
    {
        std::swap(lc(x),rc(x));
        rev[x]^=1;
    }
    inline void plus(ll x,ll k)
    {
        (Val[x]+=k)%=Mod;
        (sum[x]+=size[x]*k%Mod)%Mod;
        (add[x]+=k)%=Mod;
    }
    inline void multi(ll x,ll k)
    {
        (Val[x]*=k)%=Mod;
        (sum[x]*=k)%=Mod;
        (add[x]*=k)%=Mod;(mul[x]*=k)%=Mod;
    }
    inline void pushup(ll x)
    {
        size[x]=size[lc(x)]+size[rc(x)]+1;
        sum[x]=(sum[lc(x)]+sum[rc(x)]+Val[x])%Mod;
    }
    inline void pushdown(ll x)
    {
        if(mul[x]!=1)
        {
            multi(lc(x),mul[x]);multi(rc(x),mul[x]);
            mul[x]=1;
        }
        if(add[x])
        {
            plus(lc(x),add[x]);plus(rc(x),add[x]);
            add[x]=0;
        }
        if(rev[x])
        {
            if(lc(x))reverse(lc(x));
            if(rc(x))reverse(rc(x));
            rev[x]=0;
        }
    }
    inline void rotate(ll x)
    {
        ll f=fa[x],p=fa[f],c=(rc(f)==x);
        if(nroot(f))ch[p][rc(p)==f]=x;
        fa[ch[f][c]=ch[x][c^1]]=f;
        fa[ch[x][c^1]=f]=x;
        fa[x]=p;
        pushup(f);
        pushup(x);
    }
    inline void splay(ll x)
    {
        std::stack<ll> s;
        s.push(x);
        for(register int i=x;nroot(i);i=fa[i])s.push(fa[i]);
        while(!s.empty())pushdown(s.top()),s.pop();
        for(register int y=fa[x];nroot(x);rotate(x),y=fa[x])
            if(nroot(y))rotate((lc(y)==x)==(lc(fa[y])==y)?y:x);
        pushup(x);
    }
    inline void access(ll x)
    {
        for(register ll y=0;x;x=fa[y=x])splay(x),rc(x)=y,pushup(x);
    }
    inline void makeroot(ll x)
    {
        access(x);splay(x);reverse(x);
    }
    inline void split(ll x,ll y)
    {
        makeroot(x);access(y);splay(y);
    }
    inline void link(ll x,ll y)
    {
        makeroot(x);fa[x]=y;
    }
    inline void cut(ll x,ll y)
    {
        split(x,y);fa[x]=lc(y)=0;pushup(y);
    }
};
LCT T;
template<typename T> inline void read(T &x)
{
    T data=0,w=1;
    char ch=0;
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')w=-1,ch=getchar();
    while(ch>='0'&&ch<='9')data=((T)data<<3)+((T)data<<1)+(ch^'0'),ch=getchar();
    x=data*w;
}
template<typename T> inline void write(T x,char c='\0')
{
    if(x<0)putchar('-'),x=-x;
    if(x>9)write(x/10);
    putchar(x%10+'0');
    if(c!='\0')putchar(c);
}
template<typename T> inline void chkmin(T &x,T y){x=(y<x?y:x);}
template<typename T> inline void chkmax(T &x,T y){x=(y>x?y:x);}
template<typename T> inline T min(T x,T y){return x<y?x:y;}
template<typename T> inline T max(T x,T y){return x>y?x:y;}
int main()
{
    read(n);read(q);
    T.init();
    for(register int i=1;i<n;++i)
    {
        ll u,v;
        read(u);read(v);
        T.link(u,v);
    }
    while(q--)
    {
        char opt;
        std::cin>>opt;
        if(opt=='+')
        {
            ll u,v,c;
            read(u);read(v);read(c);
            T.split(u,v);T.plus(v,c);
        }
        if(opt=='*')
        {
            ll u,v,c;
            read(u);read(v);read(c);
            T.split(u,v);T.multi(v,c);
        }
        if(opt=='/')
        {
            ll u,v;
            read(u);read(v);
            T.split(u,v);
            write(T.sum[v],'\n');
        }
        if(opt=='-')
        {
            ll u1,v1,u2,v2;
            read(u1);read(v1);read(u2);read(v2);
            T.cut(u1,v1);T.link(u2,v2);
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/hongyj/p/8688060.html

时间: 2024-11-09 04:58:28

【刷题】洛谷 P1501 [国家集训队]Tree II的相关文章

洛谷P1501 [国家集训队]Tree II【LCT】

题目描述 一棵n个点的树,每个点的初始权值为1.对于这棵树有q个操作,每个操作为以下四种操作之一: u v c:将u到v的路径上的点的权值都加上自然数c: u1 v1 u2 v2:将树中原有的边(u1,v1)删除,加入一条新边(u2,v2),保证操作完之后仍然是一棵树: u v c:将u到v的路径上的点的权值都乘上自然数c: / u v:询问u到v的路径上的点的权值和,求出答案对于51061的余数. 输入格式: 第一行两个整数n,q 接下来n-1行每行两个正整数u,v,描述这棵树 接下来q行,每

洛谷 P1501 [国家集训队]Tree II

看来这个LCT板子并没有什么问题 1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 typedef long long LL; 5 const LL md=51061; 6 namespace LCT 7 { 8 struct Node 9 { 10 Node *ch[2],*fa; 11 bool rev; 12 LL addv,mulv; 13 LL dat,sum,sz; 14 void pad

洛谷P1501 [国家集训队]Tree II

关于LCT的其它问题可以参考一下我的LCT总结 一道LCT很好的练习放懒标记技巧的题目. 一开始看到又做加法又做乘法的时候我是有点mengbi的. 然后我想起了模板线段树2......(相信各位Dalao一定做过这道题) 这里的维护懒标记方法很像.除了翻转标记以外还要维护乘法标记和加法标记. 根据运算优先级,乘法是要先算的,所以先放,放的时候子树的\(sum\),乘法标记,加法标记,儿子的\(val\)统统都要乘一遍. 放加法标记的时候,想到线段树的区间大小是稳定的,而Splay并不是,所以还要

P1501 [国家集训队]Tree II

做法 P1501 [国家集训队]Tree II 树上懒惰标记维护动态路径模板题 做法 其实做这题也能练一下对\(LCT\)的了解 我们对\(x,y\)这条路径修改时:\(Split(x,y);....(y)\),传到\(y\)上去就行了 我们发现不管什么操作都会用到\(Access\),其中把底下的点上旋\(splay\)的同时会把\(y\)上的标记取下来 和线段树差不多吧不知道为什么是道黑题 My complete code 写代码(15min)+调试(15min)感觉比模板还容易打 #inc

洛谷 1775. [国家集训队2010]小Z的袜子

1775. [国家集训队2010]小Z的袜子 ★★★   输入文件:hose.in   输出文件:hose.out   简单对比时间限制:1 s   内存限制:512 MB [题目描述] 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命…… 具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,

题解 luogu P1501【[国家集训队]Tree II】(Link-Cut-Tree)

Link-Cut-Tree 的懒标记下传正确食用方法. 我们来逐步分析每一个操作. 1:+ u v c:将u到v的路径上的点的权值都加上自然数c; 解决方法: 很显然,我们可以 split(u,v) 来提取u,v这一段区间,提取完了将 Splay(v),然后直接在v上打加法标记add即可. 代码: inline void pushadd(ll x,ll val){//打标记 s[x]+=sz[x]*val,v[x]+=val,add[x]+=val; s[x]%=MOD,v[x]%=MOD,ad

[国家集训队]Tree II

题目 这不是线段树模板2放\(lct\)上了吗 于是开始码码码 之后一直wawawa 于是开始调调调 之后旁边的慎老师看了一看代码就说,你下放乘法标记的时候不乘加法标记吗 我:... 代码 #include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #define maxn 300005 #define re register #define LL long long #

leetcode 刷题之路 64 Construct Binary Tree from Inorder and Postorder Traversal

Given inorder and postorder traversal of a tree, construct the binary tree. Note: You may assume that duplicates do not exist in the tree. 给出二叉树的中序遍历和后序遍历结果,恢复出二叉树. 后序遍历序列的最后一个元素值是二叉树的根节点的值,查找该元素在中序遍历序列中的位置mid,根据中序遍历和后序遍历性质,有: 位置mid以前的序列部分为二叉树根节点左子树中

【leetcode刷题笔记】Validate Binary Search Tree

Given a binary tree, determine if it is a valid binary search tree (BST). Assume a BST is defined as follows: The left subtree of a node contains only nodes with keys less than the node's key. The right subtree of a node contains only nodes with keys