题目大意

“将世界终结前最后的画面,深深刻印进死水般的心海.”

祈愿没有得到回应,雷声冲破云霄,正在祈愿的洛天依受到了极大的打击。

洛天依叹了口气,说:”看来这个世界正如我之前所说的一样,早已失去一切生机”

你沉默了下来,没有说什么话,只是静静地坐在洛天依的身旁,一同观赏这末日之景.

天空被云朵覆盖,一朵具有强大能量的云映入你们的眼帘,这是始云!是抽取世界能量的最重要的一朵云!但是洛天依明显没有破坏这朵云的斗志了,你也只好静静地观赏.

这个世界被吸取的能量从始云开始传递,通过愿银线输送到各个积雨云上,每朵积雨云即作为能量中转点,又作为能量湮灭点。如此,整片天空构成了一个巨大的能量输送网络。很巧,我们可以用图论中的有向图来描述这个网络.

我们定义始云的编号为1.

对于一朵积雨云y,如果从始云开始到这个点的所有路径都经过了点x,那么我们称x是y的支配云,x支配y,y被x支配。显然一个点的支配云可能有多个且至少有两个。

旁边的洛天依此时非常惊讶,因为她发现不同的积雨云的能量强度不一样!

洛天依用正整数来描述这个能量的强度,同时起名为湮灭能量数.

也就是说在这个云朵湮灭的能量的单位数目.

然而整个网络中的云湮灭的能量数在不断的变化中,变化的情况如下:

C 1 u w有一股能量在云朵u中湮灭了w的能量.

C 2 u w点u支配的所有云朵上都湮灭了w能量

C 3 u w支配点u的所有的云朵上都湮灭了w的能量

“最后一次求你了,为我推演一下这毁灭能量的变化吧.”

洛天依一共有四种请求:

Q 1 u 询问u支配的所有的云朵的湮灭能量数之和.

Q 2 u 询问支配u的所有的云朵的湮灭能量数权值之和.

Q 3 s x1 x2 ... xs 询问所有支配了其中任一云朵的云朵的湮灭能量数权值之和.

R k让状态回到k次变化之前.若k大于已经执行的操作数,则视为回到初始状态.

你自然不忍心拒绝洛天依,于是你打算圆满完成这个任务.

分析

支配树裸题,

先构出支配树,然后树链剖分。

经典的树链剖分操作。

注:目前我还没有打关于支配树的介绍,因为我还不是理解透彻,本月内我将会打一篇支配树的blog。

#include <cmath>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <bitset>
#include <set>
#include <vector>
#define clear(a) memset(a,0,sizeof(a))
const int inf=2147483647;
const int mo=1e9+7;
const int N=100005;
using namespace std;
int head[N], pre[N], dom[N], to[N*20], nt[N*20], tot,lt[N],w[N];
void link(int *h,int fr,int tt)
{
tot ++;
nt[tot] = h[fr];
to[tot] = tt;
h[fr] = tot;
}
int n, m;
void init()
{
scanf("%d%d", &n, &m);
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
int a, b;
for(int i = 1; i <= m; ++i)
{
scanf("%d%d", &a, &b);
link(head, a, b);
link(pre, b, a);
}
}
int bcj[N], semi[N], idom[N], best[N], dfn[N], id[N], fa[N], num;
int push(int v)
{
if(v == bcj[v]) return v;
int y = push(bcj[v]);
if(dfn[semi[best[bcj[v]]]] < dfn[semi[best[v]]]) best[v] = best[bcj[v]];
return bcj[v] = y;
}
void dfs(int rt)
{
dfn[rt] = ++num;
id[num] = rt;
for(int i = head[rt]; i; i = nt[i])
if(!dfn[to[i]])
{
dfs(to[i]);
fa[to[i]] = rt;
} }
void tarjan()
{
for(int i = n, u; i >= 2; --i)
{
u = id[i];
for(int j = pre[u]; j; j = nt[j])
{
if(!dfn[to[j]]) continue;
push(to[j]);
if(dfn[semi[best[to[j]]]] < dfn[semi[u]]) semi[u] = semi[best[to[j]]];
}
link(dom, semi[u], u);
bcj[u] = fa[u];u = id[i - 1];
for(int j = dom[u]; j; j = nt[j])
{
push(to[j]);
if(semi[best[to[j]]] == u) idom[to[j]] = u;
else idom[to[j]] = best[to[j]];
}
}
for(int i = 2, u; i <= n; ++i)
{
u = id[i];
if(idom[u] != semi[u]) idom[u] = idom[idom[u]];
link(lt,idom[u],u);
}
}
int son[N],size[N],deep[N],top[N],en[N];
void dg(int x)
{
deep[x]=deep[fa[x]]+1;
size[x]=1;
for(int i=lt[x];i;i=nt[i])
{
int j=to[i];
if(j==fa[x]) continue;
fa[j]=x;
dg(j);
size[x]+=size[j];
if(size[j]>size[son[x]]) son[x]=j;
}
}
void dg1(int x)
{
id[++num]=x,dfn[x]=num;
if(!top[x]) top[x]=x;
if(son[x]) top[son[x]]=top[x],dg1(son[x]);
for(int i=lt[x];i;i=nt[i])
{
int j=to[i];
if(j==fa[x] || j==son[x]) continue;
dg1(j);
}
en[x]=num;
}
struct tree
{
long long s,la,l,r;
}tr[N*30];
int rt[N];
void newp(int v)
{
tr[++num]=tr[tr[v].l],tr[v].l=num;
tr[++num]=tr[tr[v].r],tr[v].r=num;
}
void down(int v,int l,int r,int mid)
{
if(!tr[v].la) return;
newp(v);
tr[tr[v].l].s+=tr[v].la*(mid-l+1);
tr[tr[v].r].s+=tr[v].la*(r-mid);
tr[tr[v].l].la+=tr[v].la,tr[tr[v].r].la+=tr[v].la;
tr[v].la=0;
return;
}
void put(int v,int l,int r,int x,int y,int z)
{
if(l==x && y==r)
{
tr[v].s+=z*(r-l+1),tr[v].la+=z;
return;
}
int mid=(l+r)>>1;
down(v,l,r,mid);
if(y<=mid) tr[++num]=tr[tr[v].l],tr[v].l=num,put(tr[v].l,l,mid,x,y,z);
else
if(x>mid) tr[++num]=tr[tr[v].r],tr[v].r=num,put(tr[v].r,mid+1,r,x,y,z);
else
tr[++num]=tr[tr[v].l],tr[v].l=num,tr[++num]=tr[tr[v].r],tr[v].r=num,
put(tr[v].l,l,mid,x,mid,z),put(tr[v].r,mid+1,r,mid+1,y,z);
tr[v].s=tr[tr[v].l].s+tr[tr[v].r].s;
}
long long find(int v,int l,int r,int x,int y)
{
if(l==x && y==r) return tr[v].s;
int mid=(l+r)>>1;
down(v,l,r,mid);
if(y<=mid) return find(tr[v].l,l,mid,x,y);
else
if(x>mid) return find(tr[v].r,mid+1,r,x,y);
else
return find(tr[v].l,l,mid,x,mid)+find(tr[v].r,mid+1,r,mid+1,y);
}
void up(int v,int x,int y)
{
for(;x;x=fa[top[x]]) put(v,1,n,dfn[top[x]],dfn[x],y);
}
long long up1(int v,int x)
{
long long ans=0;
for(;x;x=fa[top[x]]) ans+=find(v,1,n,dfn[top[x]],dfn[x]);
return ans;
}
int st[N];
bool cmp(int x,int y)
{
return dfn[x]<dfn[y];
}
int lca(int u,int v)
{
int f1=top[u],f2=top[v];
while(f1!=f2)
{
if(deep[f1]<deep[f2]) swap(f1,f2),swap(u,v);
u=fa[f1],f1=top[u];
}
if(deep[u]>deep[v]) swap(u,v);
return u;
}
int main()
{
init();
for(int i=1;i<=n;i++) bcj[i]=semi[i]=best[i]=i;
dfs(1),tarjan();
num=0;
clear(fa),clear(dfn),clear(id);
dg(1),dg1(1);
rt[0]=num=1;
for(int i=1;i<=n;i++) put(rt[0],1,n,dfn[i],dfn[i],w[i]);
scanf("%d",&m);
for(int x,y,t,now=0;m--;)
{
char ch=getchar();
for(;ch!='C' && ch!='Q' && ch!='R';) ch=getchar();
if(ch=='C')
{
scanf("%d%d%d",&t,&x,&y);
rt[++now]=++num,tr[rt[now]]=tr[rt[now-1]];
if(t==1) put(rt[now],1,n,dfn[x],dfn[x],y);
else
if(t==2) put(rt[now],1,n,dfn[x],en[x],y);
else up(rt[now],x,y);
}
else
if(ch=='Q')
{
scanf("%d%d",&t,&x);
if(t==1) printf("%lld\n",find(rt[now],1,n,dfn[x],en[x]));
else
if(t==2) printf("%lld\n",up1(rt[now],x));
else
{
long long sum=0;
for(int j=1;j<=x;j++) scanf("%d",&st[j]),sum+=up1(rt[now],st[j]);
sort(st+1,st+1+x,cmp);
for(int j=1;j<x;j++) sum-=up1(rt[now],lca(st[j],st[j+1]));
printf("%lld\n",sum);
}
}
else
{
scanf("%d",&t);
now=max(0,now-t);
}
}
}

【51nod 2004】终结之时的更多相关文章

  1. 51nod2004 终结之时 (支配树+树剖+树链的并)

    link 我永远喜欢洛天依 给定一张图世末积雨云,你需要维护其支配树: 单点修改,子树修改,树链修改 子树求和,树链求和,多条树链的并集求和 撤销之前的操作 可以先用 Lengauer-Tarjan ...

  2. 垃圾回收GC:.Net自己主动内存管理 上(三)终结器

    垃圾回收GC:.Net自己主动内存管理 上(三)终结器 垃圾回收GC:.Net自己主动内存管理 上(一)内存分配 垃圾回收GC:.Net自己主动内存管理 上(二)内存算法 垃圾回收GC:.Net自己主 ...

  3. 读《Effect Java中文版》

    读<Effect Java中文版> 译者序 序 前言 第1章引言 1   第2章创建和销毁对象 4 第1条:考虑用静态工厂方法代替构造函数 4 第2条:使用私有构造函数强化singleto ...

  4. Effective Java笔记一 创建和销毁对象

    Effective Java笔记一 创建和销毁对象 第1条 考虑用静态工厂方法代替构造器 第2条 遇到多个构造器参数时要考虑用构建器 第3条 用私有构造器或者枚举类型强化Singleton属性 第4条 ...

  5. 基础才是重中之重~C#中标准的IDispose模式

    回到目录 IDispose模式在C++中用的很多,用来清理资源,而在C#里,资源分为托管和非托管两种,托管资源是由C#的CLR帮助我们清理的,它是通过调用对象的析构函数完成的对象释放工作,而对于非托管 ...

  6. 个人阅读作业——M1/M2总结

    ~ http://www.cnblogs.com/wx1306/p/4831950.html 在这篇博客中,我提出来一些关于软件工程的问题,但随着这一个学期的即将结束,以及我对软件开发的了解的深入,我 ...

  7. Java垃圾收集学习笔记

    (1)除了释放不再被引用的对象,垃圾收集器还要处理 堆碎块 .请求分配新对象时可能不得不增大堆空间的大小,虽然可以使用的空闲空间是足够的,但是堆中没有没有连续的空间放得下新对象.可能会导致虚拟机产生不 ...

  8. EffectiveJava笔记(第一部分)

    考虑用静态构造方法代替构造器的好处: 1.静态构造方法有名字     BigInteger.probablePrime(int, int, Random)比 new BigInteger(int, i ...

  9. [Effective Java]第二章 创建和销毁对象

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

随机推荐

  1. mybatis-plus配置多数据源invalid bound statement (not found)

    mybatis-plus配置多数据源invalid bound statement (not found) 错误原因 引入mybatis-plus应该使用的依赖如下,而不是mybatis <de ...

  2. 南昌网络赛C.Angry FFF Party

    南昌网络赛C.Angry FFF Party Describe In ACM labs, there are only few members who have girlfriends. And th ...

  3. django初步了解3

    目录 JsonResponse模块 form表单上传文件 FBV与CBV django settings源码分析及实际应用 模板语法 模板传值 模板过滤器 标签 if标签 for标签 with标签 自 ...

  4. jwt认证规则

    目录 认证规则图 django不分离 drf分类 认证规则演变图 数据库session认证:低效 缓存认证:高效 jwt认证:高效 缓存认证:不易并发 jwt认证:易并发 JWT认证规则 优点 格式 ...

  5. spark教程(19)-sparkSQL 性能优化之谓词下推

    在 sql 语言中,where 表示的是过滤,这部分语句被 sql 层解析后,在数据库内部以谓词的形式出现: 在 sparkSQL 中,如果出现 where,它会现在数据库层面进行过滤,一般数据库会有 ...

  6. MySQL5.7,MySQL8 pfs,sys库使用

    MySQL5.7中performance和sys schema中的监控参数解释 在MySQL5.7中,performance schema有很大改进,包括引入大量新加入的监控项.降低占用空间和负载,以 ...

  7. 基于全备份+binlog方式恢复数据

    基于全备份+binlog方式恢复数据 将bkxt从库的全备份在rescs5上恢复一份,用cmdb操作 恢复全备后执行如下操作 set global read_only=OFF; stop slave; ...

  8. 【IntelliJ IDEA】tomcat启动,打印日志乱码问题 【最新解决方法请看最后附录】

    刚开始给idea上配置了一个tomcat,然后跟着http://wiki.jikexueyuan.com/project/intellij-idea-tutorial/theme-settings.h ...

  9. 3-Perl 基础语法

    Perl 基础语法Perl借用了C.sed.awk.shell脚本以及很多其他编程语言的特性,语法与这些语言有些类似,也有自己的特点.Perl 程序有声明与语句组成,程序自上而下执行,包含了循环,条件 ...

  10. 树莓派3B+和3B 安装64位debian GUN/Linux系统

    请直接参考如下博客: https://blog.csdn.net/u013451404/article/details/80710136 如果是3B的树莓派用户,只需要把第一个分区boot里的.dtb ...