洛谷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行,每行描述一个操作

输出格式:

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

输入样例

3 2

1 2

2 3

  • 1 3 4

    / 1 1

    输出样例

    4

    说明

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

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

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

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

    ****************************

    题目分析:

    都是LCT的基本操作

    注意下放标记先乘后加

    要开long long

这题评级竟然是深蓝???

**********************

#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long lt;

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<‘0‘||ss>‘9‘){if(ss==‘-‘)f=-1;ss=getchar();}
    while(ss>=‘0‘&&ss<=‘9‘){x=x*10+ss-‘0‘;ss=getchar();}
    return f*x;
}

const int mod=51061;
const int maxn=500010;
int n,m;
lt v[maxn];
int ch[maxn][2],fa[maxn],size[maxn];
lt sum[maxn],add[maxn],mul[maxn];
int lzy[maxn];
int st[maxn];

void update(int x)
{
    size[x]=size[ch[x][0]]+size[ch[x][1]]+1;
    sum[x]=(sum[ch[x][0]]+sum[ch[x][1]]+v[x])%mod;
}

void pushmul(int x,lt w)
{
    mul[x]*=w; mul[x]%=mod;
    add[x]*=w; add[x]%=mod;
    sum[x]*=w; sum[x]%=mod;
    v[x]*=w; v[x]%=mod;
}

void pushadd(int x,lt w)
{
    sum[x]+=w*size[x]; sum[x]%=mod;
    v[x]+=w; v[x]%=mod;
    add[x]+=w; add[x]%=mod;
}

void pushrev(int x)
{
    swap(ch[x][0],ch[x][1]);
    lzy[x]^=1;
}

void push(int x)
{
    int lc=ch[x][0],rc=ch[x][1];
    if(mul[x]!=1) pushmul(lc,mul[x]),pushmul(rc,mul[x]),mul[x]=1;
    if(add[x]) pushadd(lc,add[x]),pushadd(rc,add[x]),add[x]=0;
    if(lzy[x]) pushrev(lc),pushrev(rc),lzy[x]=0;
}

int isrt(int x)
{
    return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;
}

void rotate(int x)
{
    int y=fa[x],z=fa[y];
    int d=(ch[y][0]==x);
    if(!isrt(y))
    {
        if(ch[z][0]==y) ch[z][0]=x;
        else ch[z][1]=x;
    }
    fa[y]=x; fa[ch[x][d]]=y; fa[x]=z;
    ch[y][d^1]=ch[x][d]; ch[x][d]=y;
    update(y); update(x);
}

void splay(int x)
{
    int top=0; st[++top]=x;
    for(int i=x;!isrt(i);i=fa[i])
    st[++top]=fa[i];
    while(top) push(st[top--]);

    while(!isrt(x))
    {
        int y=fa[x],z=fa[y];
        if(!isrt(y))
        {
            if((ch[y][0]==x)^(ch[z][0]==y)) rotate(x);
            else rotate(y);
        }
        rotate(x);
    }
}

void access(int x)
{
    int t=0;
    while(x)
    {
        splay(x);
        ch[x][1]=t;
        update(x);
        t=x; x=fa[x];
    }
}

void mkrt(int x)
{
    access(x); splay(x);
    pushrev(x);
}

void match(int x,int y)
{
    mkrt(x);
    fa[x]=y;
}

void cut(int x,int y)
{
    mkrt(x);
    access(y); splay(y);
    fa[x]=0; ch[y][0]=0;
    update(y);
}

void change()
{
    int x=read(),y=read();
    cut(x,y);
    x=read(); y=read();
    match(x,y);
}

void upadd()
{
    int x=read(),y=read();lt w=read();
    mkrt(x);
    access(y); splay(y);
    pushadd(y,w);
}

void upmul()
{
    int x=read(),y=read();lt w=read();
    mkrt(x);
    access(y); splay(y);
    pushmul(y,w);
}

void get()
{
    int x=read(),y=read();
    mkrt(x);
    access(y); splay(y);
    printf("%d\n",sum[y]);
}

int main()
{
    n=read();m=read();
    for(int i=1;i<=n;++i)v[i]=size[i]=mul[i]=1;
    for(int i=1;i<n;++i)
    {
        int x=read(),y=read();
        match(x,y);
    }

    char ss;
    while(m--)
    {
        scanf("%s",&ss);
        if(ss==‘-‘) change();
        else if(ss==‘+‘) upadd();
        else if(ss==‘*‘) upmul();
        else if(ss==‘/‘) get();
    }
    return 0;
}

原文地址:https://www.cnblogs.com/niiick/p/8746744.html

时间: 2024-10-06 21:14:39

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

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

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

【刷题】洛谷 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,

洛谷 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

做法 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 #

洛谷P1182 数列分段Section II 二分答案

洛谷P1182 数列分段Section II 二分答案 题意:将 n 个 数 分为 m段 求一种方案,使这m段中最大的和 最小 额..可能有点拗口,其实就是说每一种方案,都有对应的 每段和的最大值,要求一种方案,使最大值最小 题解 :二分答案 mid为分成的最大值, 然后O(n) 判断 答案 是否可行 贪心的做下去,如果再加上就要超过了,那就新开一段 最后判断开的段数是否小于 m 1.注意要判断 如果当前这个值大于 mid,一个值就已经大于 mid了,那就直接退出了,否则 ,这个值也只会单独算为

洛谷1288 取数游戏II 博弈论

洛谷1288 取数游戏II 博弈论 最优策略 一定是你一步把值走完,然后我再走完,这样不给别人留后路 然后这样走 只要自己从左走 或者从右走其中有一个有奇数步可走,则说明是必胜局 如果都是只能走偶数步的,就是必败局 . 另一个题解 首先,对于一条链a1,a2,a3,a4......0 如果是偶数条边,那么现手一定赢,因为他每一次都只用把后面一条取完,例如 5 4 3 6 5 0 先手取完5,后手没法回到前一个位置,而无论接下来后手去多少,先手继续取完3,再然后取完5,后手没办法再去,先手赢.就这