bzoj 2631 Tree [Link-Cut Tree]

一道关于图论和数据结构的编程题,使用Link-Cut Tree处理树上权值操作,包括加、乘、询问路径权值和。题目要求在给定的一棵初始权值为1的树上进行加、乘、边替换及询问路径权值和的操作,并给出样例输入输出及数据规模约束。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2631: tree

Time Limit: 30 Sec Memory Limit: 128 MB
Submit: 3569 Solved: 1191

Description

 一棵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的余数。

Input

  第一行两个整数n,q
接下来n-1行每行两个正整数u,v,描述这棵树
接下来q行,每行描述一个操作
Output

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

3 2

1 2

2 3

*1 3 4

/ 1 1

Sample Output

4

HINT

数据规模和约定

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


真-模板题!
两个标记像那道线段树一样写。
然后一直T的原因:upgrade的时候把原来树结构上面的所有Auxiliary Tree都pushdown了!!!!上一道题居然没有T。。。。
然后输出的时候!!一定要注意特判0的情况!!!!!!!!!!!!!!
另外这道题用unsigned可以节省一半的时间。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<climits>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
typedef unsigned LL;
const int INF=0x3f3f3f3f;
const int maxn = 100005;
const LL mod = 51061;
struct Node
{
    int fa;
    int ch[2];
    bool isroot;
    LL size;
    LL sum,val;
    LL mult,add;
    int rev;
    Node() 
    {
        fa=ch[0]=ch[1]=0;
        size=1u;
        sum=0u,val=1u;
        mult=1u; add=0u;
        rev=0;
        isroot=true;
    }
}node[maxn];
#define fa(x) node[x].fa
#define ch(x,d) node[x].ch[d]
#define isroot(x) node[x].isroot
#define size(x) node[x].size
#define sum(x) node[x].sum
#define val(x) node[x].val
#define mult(x) node[x].mult
#define add(x) node[x].add
#define rev(x) node[x].rev
inline void update(int x)
{
    size(x)=1u; sum(x)=val(x);
    if(ch(x,0)) size(x)+=size(ch(x,0)) , sum(x)+=sum(ch(x,0));
    if(ch(x,1)) size(x)+=size(ch(x,1)) , sum(x)+=sum(ch(x,1));
    size(x)%=mod; sum(x)%=mod;
}
inline void rotate(int x)
{
    int y=fa(x),z=fa(y);
    int l=(ch(y,1)==x),r=l^1;
    if(isroot(y)) isroot(y)=false,isroot(x)=true;
    else ch(z,ch(z,1)==y)=x;
    fa(ch(x,r))=y; fa(y)=x; fa(x)=z;
    ch(y,l)=ch(x,r); ch(x,r)=y;
    update(y); update(x);
}
inline void pushdown(int x)
{
    if(!x) return;
    if(rev(x))
    {
        swap(ch(x,0),ch(x,1));
        if(ch(x,0)) rev(ch(x,0))^=1;
        if(ch(x,1)) rev(ch(x,1))^=1;
        rev(x)^=1;
    }
    if(ch(x,0))
    {
        sum(ch(x,0)) = (sum(ch(x,0))*mult(x)+size(ch(x,0))*add(x)%mod) % mod;
        val(ch(x,0)) = (val(ch(x,0))*mult(x) + add(x)) % mod;
        mult(ch(x,0)) = mult(ch(x,0))*mult(x) % mod;
        add(ch(x,0)) = (add(ch(x,0))*mult(x)+add(x)) % mod;
    }
    if(ch(x,1))
    {
        sum(ch(x,1)) = (sum(ch(x,1))*mult(x)+size(ch(x,1))*add(x)%mod) % mod;
        val(ch(x,1)) = (val(ch(x,1))*mult(x) + add(x)) % mod;
        mult(ch(x,1)) = mult(ch(x,1))*mult(x) % mod;
        add(ch(x,1)) = (add(ch(x,1))*mult(x)+add(x)) % mod;
    }
    mult(x)=1u; add(x)=0u;
}
int sta[maxn];
int top;
inline void upgrade(int x)
{
    while(!isroot(x))
    {
        sta[++top]=x;
        x=fa(x);
    }
    sta[++top]=x;
    while(top) pushdown(sta[top--]);
}
inline void splay(int x)
{
    upgrade(x);
    while(!isroot(x))
    {
        int y=fa(x),z=fa(y);
        if(!isroot(y))
            if(ch(y,0)==x ^ ch(z,0)==y) rotate(x);
            else rotate(y);
        rotate(x);
    }
}
inline int access(int x)
{
    int y=0;
    do
    {
        splay(x);
        isroot(ch(x,1))=true;
        isroot(ch(x,1)=y)=false;
        update(x);
        x=fa(y=x);
    }while(x);
    return y;
}
inline void makeroot(int u)
{
    int rt=access(u);
    rev(rt)^=1;
}
void cut(int u,int v)
{
    makeroot(u);
    access(v); splay(v);
    isroot(ch(v,0))=true;
    fa(ch(v,0))=0; ch(v,0)=0;
    update(v);
}
void join(int u,int v)
{
    access(u); splay(u);
    rev(u)^=1;
    fa(u)=v;
}
void modify(int u,int v,LL ad,LL mul)
{
    ad%=mod; mul%=mod;
    makeroot(u);
    access(v); splay(v);
    sum(v) = (sum(v)*mul + size(v)*ad%mod) % mod;
    val(v) = (val(v)*mul+ad) % mod;
    mult(v) = (mult(v)*mul) % mod;
    add(v) = (add(v)*mul+ad) % mod;
}
LL query(int u,int v)
{
    makeroot(u);
    access(v); splay(v);
    return sum(v);
}
inline char readchar()
{
    char c=getchar();
    while(c^'+' && c^'-' && c^'*' && c^'/') c=getchar();
    return c;
}
inline void read(int &x)
{
    x=0;
    char c=getchar();
    while(c<'0' || c>'9') c=getchar();
    while(c>='0' && c<='9')
    {
        x*=10; x+=c-'0';
        c=getchar();
    }
}
char ans[15];
int pos;
inline void readout(LL x)
{
    if(!x) // extremely important !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    {
        putchar('0');
        putchar('\n');
        return;
    }
    pos=0;
    while(x)
    {
        ans[++pos]=x%10u + '0';
        x/=10u;
    }
    while(pos)
    {
        putchar(ans[pos]);
        pos--;
    }
    putchar('\n');
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    #endif
    int n,q;
    read(n),read(q);
    for(int i=1;i<n;i++)
    {
        int x,y;
        read(x),read(y);
        join(x,y);
    }
    for(int i=1;i<=q;i++)
    {
        char c=readchar();
        int u,v,x,y;
        read(u),read(v);
        switch(c)
        {
            case '+': read(x);
                      modify(u,v,x,1);
                      break;
            case '*': read(x);
                      modify(u,v,0,x);
                      break;
            case '-': read(x),read(y);
                      cut(u,v);
                      join(x,y);
                      break;
            case '/': readout(query(u,v)); break;
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值