题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=3589

题意:给出一棵有根树,两种操作:(1)以u为根的子树所有节点权值加上一个数字;(2)给出若干个链,求这些链的节点的权值和。重复的节点的权值只计算一次。

思路:AAA树:每个节点有四个孩子,0和1是动态树的左右孩子,其他的孩子弄成一个二叉树保存在2 3号节点上。

void add(i64 &x,i64 y)
{
x=(x+y)&(mod-1);
} struct node
{
node *c[4],*p; i64 chainMark,treeMark;
int size;
i64 sum; int inner,rev;
i64 val; node()
{
c[0]=c[1]=c[2]=c[3]=p=0;
inner=1;
rev=0;
val=0;
sum=0;
size=1;
} node(int x)
{
c[0]=c[1]=c[2]=c[3]=p=0;
inner=0;
rev=0;
val=x;
size=1;
pushUp();
} void pushUp()
{
if(!inner) sum=val,size=1;
else size=1;
int i;
for(i=0;i<2;i++) if(c[i])
{
size+=c[i]->size;
add(sum,c[i]->sum);
}
} void reverse()
{
swap(c[0],c[1]);
rev^=1;
} void updateChain(i64 a)
{
add(chainMark,a);
add(sum,size*a);
add(val,a);
} void updateTree(i64 a,int flag=1)
{
add(treeMark,a);
if(flag) updateChain(a);
} void pushDown()
{
if(rev)
{
if(c[0]) c[0]->reverse();
if(c[1]) c[1]->reverse();
rev=0;
}
if(treeMark)
{
int i;
for(i=0;i<4;i++) if(c[i]) c[i]->updateTree(treeMark,i>=2);
treeMark=0;
}
if(chainMark)
{
if(c[0]) c[0]->updateChain(chainMark);
if(c[1]) c[1]->updateChain(chainMark);
chainMark=0;
}
} int isRoot(int t)
{
if(t==0) return !p||(p->c[0]!=this&&p->c[1]!=this);
return !p||!p->inner||!inner;
} void setSon(node *son,int id)
{
c[id]=son;
if(son) son->p=this;
} int pos()
{
int i;
for(i=0;i<4;i++) if(p->c[i]==this) return i;
return -1;
} node* getSon(int id)
{
if(c[id]) c[id]->pushDown();
return c[id];
} int dir(int t)
{
return p->c[t+1]==this;
}
}; /**
*t=0 d=0: 右旋
*t=0 d=1: 左旋
*
* **/
void rot(node *u,int t)
{
node *p=u->p;
int d=u->dir(t);
if(p->p) p->p->setSon(u,p->pos());
else u->p=0;
p->setSon(u->c[!d+t],d+t);
u->setSon(p,!d+t);
p->pushUp();
} /**
*t=0 or 2
* **/
void splay(node *u,int t=0)
{
while(!u->isRoot(t))
{
if(u->p->isRoot(t)) rot(u,t);
else if(u->p->dir(t)==u->dir(t)) rot(u->p,t),rot(u,t);
else rot(u,t),rot(u,t);
}
u->pushUp();
} /**
*把节点u的父亲设为v
* **/
void add(node *u,node *v)
{
v->pushDown();
int i;
for(i=2;i<4;i++) if(!v->c[i])
{
v->setSon(u,i);
return;
} node *tmp=v,*x=new node;
while(tmp->c[2]->inner) tmp=tmp->getSon(2);
x->setSon(tmp->c[2],2);
x->setSon(u,3);
tmp->setSon(x,2);
splay(x,2);
} /**
*将节点u从其父节点断开
* **/
void del(node *u)
{
if(u->p->inner)
{
node *q=u->p->c[5-u->pos()];
u->p->p->setSon(q,u->p->pos());
node *tmp=u->p;
splay(u->p->p,2);
delete tmp;
}
else
{
u->p->setSon(0,u->pos());
}
u->p=0;
} node *st[N];
int top; void pushDown(node *u)
{
top=0;
while(u) st[++top]=u,u=u->p;
while(top>0) st[top]->pushDown(),top--; } void access(node *u)
{
node *v=u,*tmp;
pushDown(u);
splay(u);
if(u->c[1]) tmp=u->c[1],u->c[1]=0,add(tmp,u),u->pushUp(); while(u->p)
{
for(tmp=u->p;tmp->inner;tmp=tmp->p);
splay(tmp);
if(tmp->c[1])
{
u->p->setSon(tmp->c[1],u->pos());
splay(u->p,2);
tmp->setSon(u,1);
}
else
{
del(u);
tmp->setSon(u,1);
} u=tmp;
u->pushUp();
}
splay(v);
} void makeRoot(node *u)
{
access(u);
u->reverse();
} int n,m;
node *a[N]; vector<int> g[N];
int f[N][20];
int dep[N]; void DFS(int u,int pre,int d)
{
f[u][0]=pre;
dep[u]=d; int i;
for(i=0;i<SZ(g[u]);i++)
{
int v=g[u][i];
if(v==pre) continue;
add(a[v],a[u]);
DFS(v,u,d+1);
}
a[u]->pushUp();
} void init()
{
DFS(1,0,1);
int i,j;
for(i=1;i<20;i++) for(j=1;j<=n;j++)
{
f[j][i]=f[f[j][i-1]][i-1];
}
} int jump(int u,int x)
{
int i;
for(i=0;i<20;i++) if(x&(1<<i)) u=f[u][i];
return u;
} int getLca(int u,int v)
{
if(u==0||v==0) return 0; if(u==v) return u;
if(dep[u]<dep[v]) swap(u,v);
int det=dep[u]-dep[v];
u=jump(u,det);
if(v==u) return u;
int i;
for(i=19;i>=0;i--) if(f[u][i]&&f[v][i]&&f[u][i]!=f[v][i])
{
u=f[u][i];
v=f[v][i];
}
return f[u][0];
} struct Query
{
int u,v; void get()
{
u=getInt();
v=getInt();
if(dep[u]<dep[v]) swap(u,v);
v=f[v][0];
} int operator<(const Query &a) const
{
return dep[v]<dep[a.v];
}
}; i64 cal(int u,int v)
{
if(u==v) return 0;
v=jump(u,dep[u]-dep[v]-1); makeRoot(a[u]);
access(a[v]);
splay(a[u]);
i64 ans=a[u]->sum; return ans;
} int main()
{ n=getInt();
int i;
for(i=0;i<n-1;i++)
{
int u=getInt();
int v=getInt();
g[u].pb(v);
g[v].pb(u);
} for(i=1;i<=n;i++) a[i]=new node(0);
init(); makeRoot(a[1]); m=getInt(); while(m--)
{
int op=getInt();
if(op==0)
{
int u=getInt();
i64 det=getInt(); node *p=a[u];
access(p);
p->val+=det;
for(i=2;i<4;i++) if(p->c[i]) p->c[i]->updateTree(det);
p->pushUp();
}
else
{
int K=getInt();
Query q[10];
for(i=1;i<=K;i++) q[i].get();
sort(q+1,q+K+1); i64 ans=0;
for(i=1;i<=K;i++)
{
int j;
for(j=1;j<i;j++)
{
int lca=getLca(q[i].u,q[j].u);
if(dep[lca]>dep[q[i].v]) q[i].v=lca;
}
ans+=cal(q[i].u,q[i].v);
} ans%=mod;
if(ans<0) ans+=mod; output(ans); puts("");
makeRoot(a[1]);
}
}
}

BZOJ 3589 动态树(子树操作,链查询)的更多相关文章

  1. BZOJ 3589 动态树 树链拆分+纳入和排除定理

    标题效果:鉴于一棵树.每个节点有一个右值,所有节点正确启动值他们是0.有两种操作模式,0 x y代表x右所有点的子树的根值添加y. 1 k a1 b1 a2 b2 --ak bk代表质疑. 共同拥有者 ...

  2. BZOJ 3589 动态树 (树链剖分+线段树)

    前言 众所周知,90%90\%90%的题目与解法毫无关系. 题意 有一棵有根树,两种操作.一种是子树内每一个点的权值加上一个同一个数,另一种是查询多条路径的并的点权之和. 分析 很容易看出是树链剖分+ ...

  3. BZOJ 3589: 动态树 树链剖分+线段树+树链的并

    利用树剖序的一些性质~ 这个题可以出到 $\sum k=10^5$ 左右. 做法很简单:每次暴力跳重链,并在线段树上查询链和. 查询之后打一个标记,把加过的链都置为 $0$.这样的话在同一次询问时即使 ...

  4. bzoj 3589: 动态树【树链剖分+容斥】

    因为一开始调试不知道unsigned怎么输出就没有加\n结果WA了一上午!!!!!然而最后放弃了unsigned选择了&2147483647 首先链剖,因为它所给的链一定是某个点到根的路径上的 ...

  5. Tsinsen A1517. 动态树 树链剖分,线段树,子树操作

    题目 : http://www.tsinsen.com/A1517 A1517. 动态树 时间限制:3.0s   内存限制:1.0GB    总提交次数:227   AC次数:67   平均分:49. ...

  6. bzoj 4034: [HAOI2015]树上操作 (树剖+线段树 子树操作)

    4034: [HAOI2015]树上操作 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 6779  Solved: 2275[Submit][Stat ...

  7. 【BZOJ-3589】动态树 树链剖分 + 线段树 + 线段覆盖(特殊的技巧)

    3589: 动态树 Time Limit: 30 Sec  Memory Limit: 1024 MBSubmit: 405  Solved: 137[Submit][Status][Discuss] ...

  8. Link Cut Tree 动态树 小结

    动态树有些类似 树链剖分+并查集 的思想,是用splay维护的 lct的根是动态的,"轻重链"也是动态的,所以并没有真正的轻重链 动态树的操作核心是把你要把 修改/询问/... 等 ...

  9. luogu3703 [SDOI2017]树点涂色(线段树+树链剖分+动态树)

    link 你谷的第一篇题解没用写LCT,然后没观察懂,但是自己YY了一种不用LCT的做法 我们考虑对于每个点,维护一个fa,代表以1为根时候这个点的父亲 再维护一个bel,由于一个颜色相同的段一定是一 ...

随机推荐

  1. sql 查看 锁定的表 或者 未提交 的事务

    --查看锁定的 表select request_session_id spid,OBJECT_NAME(resource_associated_entity_id) tableName from sy ...

  2. C#与js的各种交互

    今天遇到一个问题,查到不错的资料,放上来记录一下,以防忘记地址,算是我的笔记吧! 很多人都向在服务器端调用客户端的函数来操作,也就是在asp中调用javascript脚本中已经定义好的脚本函数.经过研 ...

  3. 自定义Writable、RawComparatorWritable、comparators(转)

    自定义Writable hadoop虽然已经实现了一些非常有用的Writable,而且你可以使用他们的组合做很多事情,但是如果你想构造一些更加复杂的结果,你可以自定义Writable来达到你的目的,我 ...

  4. 认识Swift

    Swift 是一门新的编程语言,用于编写 iOS 和 OS X 应用程序.Swift 结合了 C 和 Objective-C 的优点并且不受C兼容性的限制.Swift 使用安全的编程模式并添加了很多新 ...

  5. javascript 深拷贝

    javascript存在两种拷贝:浅拷贝.深拷贝. 它们最大的区别在于引用类型的拷贝上:浅拷贝复制的是引用(指针),深拷贝复制的是里面的数据. 由于以上原因,在下例中 浅拷贝修改的值影响了声明的对象a ...

  6. 操作系统双语阅读 - Schedulers调度器2

    Most processes can be described as either I/O bound or CPU bound. 大多数进程都可以描述为IO绑定或者CPU绑定. An I/O-bou ...

  7. Relative 定位与Absolute 定位实例

    一直没有弄懂相对定位与绝对定位之间的关系,今天特来学习一下.本实践都是在360浏览器下测试所得. <!DOCTYPE html> <html> <head> < ...

  8. Linux kernel的 Makefile和Kconfig以及Make menuconfig的关系【转】

    本文转载自:http://blog.sina.com.cn/s/blog_4ba5b45e0102e6vp.html 熟悉内核的Makefile对开发设备驱动.理解内核代码结构都是非常重要的linux ...

  9. iOS 学习笔记 七 (2015.03.29)code snippet操作

    1.code snippet 备份路径:~/Library/Developer/Xcode/UserData/CodeSnippets/

  10. armv8(aarch64)linux内核中flush_dcache_all函数详细分析【转】

    转自:http://blog.csdn.net/qianlong4526888/article/details/12062809 版权声明:本文为博主原创文章,未经博主允许不得转载. /* *  __ ...