题解 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,add[x]%=MOD;
}

inline void split(ll x,ll y){//LCT基本操作split,不再赘述
    makeroot(x);Access(y);Splay(y);
}

//(main函数中):
if(op[0]==‘+‘){
     scanf("%lld%lld%lld",&x,&y,&v);//输入信息
     split(x,y);pushadd(y,v);//提取链条&打标记
}

2:- u1 v1 u2 v2:将树中原有的边(u1,v1)删除,加入一条新边(u2,v2),保证操作完之后仍然是一棵树;

  • 解决方法:
  • 删除边即cut操作,加边即link操作。
  • 代码:
inline void link(ll x,ll y){
    makeroot(x);if(findroot(x)!=y)f[x]=y;
}
inline void cut(ll x,ll y){
    makeroot(x);split(x,y);
    if(findroot(y)==x&&f[x]==y&&!ch[x][1])
       f[x]=ch[y][0]=0;return;
}//LCT基本操作link&cut,不再赘述

//(main函数中):
if(op[0]==‘-‘){
     scanf("%lld%lld",&x,&y);cut(x,y);//删边
     scanf("%lld%lld",&x,&y);link(x,y);//加边
}

3:* u v c:将u到v的路径上的点的权值都乘上自然数c;

  • 解决方法:
  • 很显然,我们可以split(u,v)来提取u,v这一段区间,提取完了将Splay(v),然后直接在v上打乘法标记mul即可。(跟第一个操作基本同理)
  • 代码:
inline void pushmul(ll x,ll val){//打标记
     s[x]*=val,v[x]*=val,mul[x]*=val,add[x]*=val;
     s[x]%=MOD,v[x]%=MOD,mul[x]%=MOD,add[x]%=MOD;
}

//(main函数中):
if(op[0]==‘*‘){
     scanf("%lld%lld%lld",&x,&y,&v);
     split(x,y);pushmul(y,v);
}

4:/ u v:询问u到v的路径上的点的权值和,求出答案对于51061的余数。

  • 解决方法:
  • 我们可以像第1、3操作那样,先扯出这条链来(split(u,v)),因为split操作时最后Splay过了,根节点是v。而Splay的时候又将整棵Splay上的节点信息都跟新好了(懒标记都下传了),所以这棵Splay的根节点的点权即为这课Splay的点权和。而这课Splay又代表着u,v这一段区间,所以最后只需输出s[v]即可。
  • 代码:
//(main函数中):
if(op[0]==‘/‘){
     scanf("%lld%lld",&x,&y);
     split(x,y);printf("%lld\n",s[y]);
}

Code:

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define RI register ll
#define A printf("A")
#define C printf(" ")
#define MOD 51061
using namespace std;
const ll N=1e5+2;
template<typename _Tp> inline void IN(_Tp& dig){
    char c;bool flag=0;dig=0;
    while(c=getchar(),!isdigit(c))if(c==‘-‘)flag=1;
    while(isdigit(c))dig=dig*10+c-‘0‘,c=getchar();
    if(flag)dig=-dig;
}ll f[N],s[N],v[N],sz[N],rev[N],mul[N],add[N],hep[N],ch[N][2];
inline ll get(ll x){return ch[f[x]][0]==x||ch[f[x]][1]==x;}
inline ll chk(ll x){return ch[f[x]][1]==x;}
inline void pushfilp(ll x){
    swap(ch[x][0],ch[x][1]);rev[x]^=1;
}
inline void pushup(ll x){
    s[x]=(s[ch[x][0]]+s[ch[x][1]]+v[x])%MOD;
    sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1;
}
inline void pushmul(ll x,ll val){
    s[x]*=val,v[x]*=val,mul[x]*=val,add[x]*=val;
    s[x]%=MOD,v[x]%=MOD,mul[x]%=MOD,add[x]%=MOD;
}
inline void pushadd(ll x,ll val){
    s[x]+=sz[x]*val,v[x]+=val,add[x]+=val;
    s[x]%=MOD,v[x]%=MOD,add[x]%=MOD;
}
inline void pushdown(ll x){
    if(mul[x]!=1)pushmul(ch[x][0],mul[x]),pushmul(ch[x][1],mul[x]);
    if(add[x])pushadd(ch[x][0],add[x]),pushadd(ch[x][1],add[x]);
    if(rev[x]){
        if(ch[x][0])pushfilp(ch[x][0]);
        if(ch[x][1])pushfilp(ch[x][1]);
    }rev[x]=0,add[x]=0,mul[x]=1;return;
}
inline void rotate(ll x){
    ll y=f[x],z=f[y],k=chk(x),v=ch[x][!k];
    if(get(y))ch[z][chk(y)]=x;ch[x][!k]=y,ch[y][k]=v;
    if(v)f[v]=y;f[y]=x,f[x]=z;pushup(y),pushup(x);
}
inline void Splay(ll x){
    ll y=x,top=0;hep[++top]=y;
    while(get(y))hep[++top]=y=f[y];
    while(top)pushdown(hep[top--]);
    while(get(x)){
        y=f[x],top=f[y];
        if(get(y))rotate((ch[y][0]==x)^(ch[top][0]==y)?y:x);
        rotate(x);
    }pushup(x);return;
}
inline void Access(ll x){
    for(register ll y=0;x;x=f[y=x])
      Splay(x),ch[x][1]=y,pushup(x);
}
inline ll findroot(ll x){
    Access(x);Splay(x);
    while(ch[x][0])pushdown(x),x=ch[x][0];
    return x;
}
inline void makeroot(ll x){
    Access(x);Splay(x);pushfilp(x);
}
inline void split(ll x,ll y){
    makeroot(x);Access(y);Splay(y);
}
inline void link(ll x,ll y){
    makeroot(x);if(findroot(x)!=y)f[x]=y;
}
inline void cut(ll x,ll y){
    makeroot(x);split(x,y);
    if(findroot(y)==x&&f[x]==y&&!ch[x][1])
       f[x]=ch[y][0]=0;return;
}char op[2];
int main(){
    ll n,m,x,y;scanf("%lld%lld",&n,&m);
    for(register int i=1;i<=n;++i)
       mul[i]=sz[i]=v[i]=1;ll v;
    for(register int i=1;i<n;++i)
       scanf("%lld%lld",&x,&y),link(x,y);
    for(register int i=1;i<=m;++i){
        scanf("%s",op);
        if(op[0]==‘+‘){
            scanf("%lld%lld%lld",&x,&y,&v);
            split(x,y);pushadd(y,v);
        }else if(op[0]==‘-‘){
            scanf("%lld%lld",&x,&y);cut(x,y);
            scanf("%lld%lld",&x,&y);link(x,y);
        }else if(op[0]==‘*‘){
            scanf("%lld%lld%lld",&x,&y,&v);
            split(x,y);pushmul(y,v);
        }else if(op[0]==‘/‘){
            scanf("%lld%lld",&x,&y);
            split(x,y);printf("%lld\n",s[y]);
        }
    }return 0;
}

因为51061 * 5106是会越过int界限的,所以我开的longlong(当然也可以开无符号int)

我居然因为没开longlong调了两个多小时.....

原文地址:https://www.cnblogs.com/K-Qiuly/p/10205014.html

时间: 2024-10-29 04:48:49

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

POJ 题目3237 Tree(Link Cut Tree边权变相反数,求两点最大值)

Tree Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 6131   Accepted: 1682 Description You are given a tree with N nodes. The tree's nodes are numbered 1 through N and its edges are numbered 1 through N ? 1. Each edge is associated with

P1501 [国家集训队]Tree II

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

【题解】P1407国家集训队稳定婚姻

[题解][P1407 国家集训队]稳定婚姻 很好的一道建模+图论题. 婚姻关系?很像二分图匹配呀,不过不管怎么办先建模再说.婚姻关系显然用图方面的知识解决.建图! 它给定的是字符串,所以我们使用\(ac\)自动机\(map\)作匹配建点. 题意就是给你\(n\)对夫妻关系和\(m\)对情人关系,已知情人关系都可以结婚,现在假设对于某个婚姻,如果离婚,这\(2n\)个人最终依然能够结合成\(n\)对情侣,那么这样的婚姻是不稳定的.现在问每个婚姻关系的稳定性. 考虑什么样的婚姻关系是不稳定的.题目给

P3690 【模板】Link Cut Tree (动态树)

P3690 [模板]Link Cut Tree (动态树) https://www.luogu.org/problemnew/show/P3690 分析: LCT模板 代码: 注意一下cut! 1 #include<cstdio> 2 #include<algorithm> 3 4 using namespace std; 5 6 const int N = 300100; 7 8 int val[N],fa[N],ch[N][2],rev[N],sum[N],st[N],top;

Codeforces Round #339 (Div. 2) A. Link/Cut Tree

A. Link/Cut Tree Programmer Rostislav got seriously interested in the Link/Cut Tree data structure, which is based on Splay trees. Specifically, he is now studying the expose procedure. Unfortunately, Rostislav is unable to understand the definition

AC日记——【模板】Link Cut Tree 洛谷 P3690

[模板]Link Cut Tree 思路: LCT模板: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 300005 int n,m,val[maxn]; int top,ch[maxn][2],f[maxn],xr[maxn],q[maxn],rev[maxn]; inline void in(int &now) { int if_z=1;now=0; char Cget=getchar(); while

bzoj2049 [Sdoi2008]Cave 洞穴勘测 link cut tree入门

link cut tree入门题 首先说明本人只会写自底向上的数组版(都说了不写指针.不写自顶向下QAQ……) 突然发现link cut tree不难写... 说一下各个函数作用: bool isroot(int x):判断x是否为所在重链(splay)的根 void down(int x):下放各种标记 void rotate(int x):在x所在重链(splay)中将x旋转到fa[x]的位置上 void splay(int x):在x坐在重链(splay)中将x旋转到根 void acce

脑洞大开加偏执人格——可持久化treap版的Link Cut Tree

一直没有点动态树这个科技树,因为听说只能用Splay,用Treap的话多一个log.有一天脑洞大开,想到也许Treap也能从底向上Split.仔细思考了一下,发现翻转标记不好写,再仔细思考了一下,发现还是可以写的,只需要实时交换答案二元组里的两棵树,最后在吧提出来的访问节点放回去就行了.本着只学一种平衡树的想法,脑洞大开加偏执人格的开始写可持久化Treap版的Link Cut Tree... 写了才发现,常数硕大啊!!!代码超长啊!!!因为merge是从上到下,split从下到上,pushdow

link cut tree 入门

鉴于最近写bzoj还有51nod都出现写不动的现象,决定学习一波厉害的算法/数据结构. link cut tree:研究popoqqq那个神ppt. bzoj1036:维护access操作就可以了. #include<cstdio> #include<cstring> #include<cctype> #include<algorithm> #include<queue> using namespace std; #define rep(i,s,

HDOJ 题目3966 Aragorn&#39;s Story(Link Cut Tree成段加减点权,查询点权)

Aragorn's Story Time Limit: 10000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 5505    Accepted Submission(s): 1441 Problem Description Our protagonist is the handsome human prince Aragorn comes from The Lor