题目大意:
  给你一棵树,一开始每个点的权值都是0,要求支持一下三种操作:
    1.路径加等差数列。
    2.路径求和。
    3.回到以前的某次操作。
  强制在线。

思路:
  树链剖分+主席树。
  最坏情况下,n个点的树最多会被分成n-1个链,
  这里不能每个点都开一个主席树,因为主席树中要存每个线段树的根结点编号,总共有m次操作,
  因此最坏情况下,总共要存nm个根结点,显然会爆空间,因此我们可以考虑将所有点合并在一个主席树中。
  路径加等差数列时,我们可以先求出两个端点x和y上加的值ax和ay,然后往上爬的过程中根据跳过的长度维护ax和ay即可。
  交换x和y的时候就相当于翻转等差数列,只要交换ax和ay并对公差b取反即可。
  然后随随便便就跑了Rank2(Rank2的vjudge7和Rank3的skylee都是我的程序),0.63s。
  后来想抢Rank发现刷不上去了(似乎CodeChef是根据第一次交的程序来排名的)。
  交的时候发现忘记处理强制在线的操作也能AC?

细节:
  题目中回退操作以后并不能删掉中间被跳过的操作,比如从第四次操作回退到第三次操作,如果再进行一次修改,那么这个修改操作就是第五次操作。

 #include<cstdio>
#include<cctype>
#include<vector>
#include<cstring>
inline int getint() {
char ch;
while(!isdigit(ch=getchar()));
int x=ch^'';
while(isdigit(ch=getchar())) x=(((x<<)+x)<<)+(ch^'');
return x;
}
const int V=,logV=,M=;
std::vector<int> e[V];
inline void add_edge(const int u,const int v) {
e[u].push_back(v);
}
int par[V],size[V],son[V],top[V],dep[V],id[V],cnt;
void dfs1(const int x,const int p) {
dep[x]=dep[p]+;
par[x]=p;
size[x]=;
for(unsigned i=;i<e[x].size();i++) {
int &y=e[x][i];
if(y==p) continue;
dfs1(y,x);
size[x]+=size[y];
if(size[y]>size[son[x]]) son[x]=y;
}
}
void dfs2(const int x) {
top[x]=x==son[par[x]]?top[par[x]]:x;
id[x]=++cnt;
if(son[x]) dfs2(son[x]);
for(unsigned i=;i<e[x].size();i++) {
int &y=e[x][i];
if(y==par[x]||y==son[x]) continue;
dfs2(y);
}
}
class FotileTree {
private:
long long first[M*logV],diff[M*logV],sum[M*logV];
int left[M*logV],right[M*logV];
int sz;
int newnode() {
return ++sz;
}
void push_up(const int p,const int b,const int e) {
sum[p]=sum[left[p]]+sum[right[p]]+(first[p]*+(e-b)*diff[p])*(e-b+)/;\
}
public:
int root[M];
void modify(int &p,const int old_p,const int b,const int e,const int l,const int r,const long long x,const long long y) {
if(!p||p==old_p) p=newnode();
first[p]=first[old_p];
diff[p]=diff[old_p];
if((b==l)&&(e==r)) {
first[p]+=x;
diff[p]+=y;
if(!left[p]) left[p]=left[old_p];
if(!right[p]) right[p]=right[old_p];
push_up(p,b,e);
return;
}
int mid=(b+e)>>;
if(l<=mid) modify(left[p],left[old_p],b,mid,l,std::min(mid,r),x,y);
if(r>mid) modify(right[p],right[old_p],mid+,e,std::max(mid+,l),r,x+(std::max(mid+,l)-l)*y,y);
if(!left[p]) left[p]=left[old_p];
if(!right[p]) right[p]=right[old_p];
push_up(p,b,e);
}
long long query(const int p,const int b,const int e,const int l,const int r) {
if(!p) return ;
if((b==l)&&(e==r)) return sum[p];
int mid=(b+e)>>;
long long ret=(first[p]*+(l+r-b*)*diff[p])*(r-l+)/;
if(l<=mid) ret+=query(left[p],b,mid,l,std::min(mid,r));
if(r>mid) ret+=query(right[p],mid+,e,std::max(mid+,l),r);
return ret;
}
};
FotileTree t;
inline int get_lca(int x,int y) {
while(top[x]!=top[y]) {
if(dep[top[x]]<dep[top[y]]) std::swap(x,y);
x=par[top[x]];
}
if(dep[x]>dep[y]) std::swap(x,y);
return x;
}
int n;
inline void modify(const int old_root,int &root,int x,int y,const long long a,long long b) {
int lca=get_lca(x,y);
int dis=dep[x]+dep[y]-dep[lca]*;
int ax=a,ay=a+b*dis;
while(top[x]!=top[y]) {
if(dep[top[x]]<dep[top[y]]) {
std::swap(x,y);
std::swap(ax,ay);
b=-b;
}
t.modify(root,old_root,,n,id[top[x]],id[x],ax+(dep[x]-dep[top[x]])*b,-b);
ax+=(dep[x]-dep[top[x]]+)*b;
x=par[top[x]];
}
if(dep[x]<dep[y]) {
std::swap(x,y);
std::swap(ax,ay);
b=-b;
}
t.modify(root,old_root,,n,id[y],id[x],ax+(dep[x]-dep[y])*b,-b);
}
inline long long query(const int root,int x,int y) {
long long ret=;
while(top[x]!=top[y]) {
if(dep[top[x]]<dep[top[y]]) std::swap(x,y);
ret+=t.query(root,,n,id[top[x]],id[x]);
x=par[top[x]];
}
if(dep[x]<dep[y]) std::swap(x,y);
ret+=t.query(root,,n,id[y],id[x]);
return ret;
}
inline void rollback(int &cur,const int x) {
cur=x;
}
int main() {
n=getint();
int m=getint();
for(int i=;i<n;i++) {
int u=getint(),v=getint();
add_edge(u,v);
add_edge(v,u);
}
dfs1(,);
dfs2();
long long lastans=;
int cnt_c=,cur=;
while(m--) {
char op[];
scanf("%1s",op);
switch(op[]) {
case 'c': {
int x=(getint()+lastans)%n+,y=(getint()+lastans)%n+,a=getint(),b=getint();
cnt_c++;
t.root[cnt_c]=;
modify(t.root[cur],t.root[cnt_c],x,y,a,b);
cur=cnt_c;
break;
}
case 'q': {
int x=(getint()+lastans)%n+,y=(getint()+lastans)%n+;
printf("%lld\n",lastans=query(t.root[cur],x,y));
break;
}
case 'l': {
int x=(getint()+lastans)%(cnt_c+);
rollback(cur,x);
break;
}
}
}
return ;
}

[CodeChef-QUERY]Observing the Tree的更多相关文章

  1. Codechef Observing the Tree

    Home » Practice(Hard) » Observing the Tree   https://www.codechef.com/problems/QUERY Observing the T ...

  2. BZOJ 3221: [Codechef FEB13] Obserbing the tree树上询问( 可持久化线段树 + 树链剖分 )

    树链剖分+可持久化线段树....这个一眼可以看出来, 因为可持久化所以写了标记永久化(否则就是区间修改的线段树的持久化..不会), 结果就写挂了, T得飞起...和管理员拿数据调后才发现= = 做法: ...

  3. Query on a tree——树链剖分整理

    树链剖分整理 树链剖分就是把树拆成一系列链,然后用数据结构对链进行维护. 通常的剖分方法是轻重链剖分,所谓轻重链就是对于节点u的所有子结点v,size[v]最大的v与u的边是重边,其它边是轻边,其中s ...

  4. SPOJ 375. Query on a tree (树链剖分)

    Query on a tree Time Limit: 5000ms Memory Limit: 262144KB   This problem will be judged on SPOJ. Ori ...

  5. QTREE3 spoj 2798. Query on a tree again! 树链剖分+线段树

    Query on a tree again! 给出一棵树,树节点的颜色初始时为白色,有两种操作: 0.把节点x的颜色置反(黑变白,白变黑). 1.询问节点1到节点x的路径上第一个黑色节点的编号. 分析 ...

  6. spoj 375 Query on a tree(树链剖分,线段树)

      Query on a tree Time Limit: 851MS   Memory Limit: 1572864KB   64bit IO Format: %lld & %llu Sub ...

  7. bzoj 3637: Query on a tree VI 树链剖分 && AC600

    3637: Query on a tree VI Time Limit: 8 Sec  Memory Limit: 1024 MBSubmit: 206  Solved: 38[Submit][Sta ...

  8. 动态树(Link Cut Tree) :SPOJ 375 Query on a tree

    QTREE - Query on a tree #number-theory You are given a tree (an acyclic undirected connected graph) ...

  9. hdu 4836 The Query on the Tree(线段树or树状数组)

    The Query on the Tree Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Ot ...

  10. QTREE - Query on a tree

    QTREE - Query on a tree 题目链接:http://www.spoj.com/problems/QTREE/ 参考博客:http://blog.sina.com.cn/s/blog ...

随机推荐

  1. python模块-platform

    #author:Blood_Zero #coding:utf- import platform print dir(platform) #获取platform函数功能 platform.archite ...

  2. linux系统文件

    关于linux系统文件命令 (1)Linux的文件系统目录配置要遵循FHS规范,规范定义的两级目录规范如下:        /home  每个账号在该目录下都有一个文件夹,进行数据的管理        ...

  3. spin_lock & mutex_lock的区别? 【转】

    转自:http://blog.csdn.net/wilsonboliu/article/details/19190861 本文由该问题引入到内核锁的讨论,归纳如下   为什么需要内核锁? 多核处理器下 ...

  4. docker 错误排查:无法进入容器.

    docker 错误排查:无法进入容器. #docker exec -it 3c1d bash rpc error: code = 2 desc = oci runtime error: exec fa ...

  5. swagger学习

    https://segmentfault.com/a/1190000010144742 https://segmentfault.com/a/1190000014775124 https://blog ...

  6. Java集合之Collection与之子类回顾

    Java学习这么久,打算这几天回顾下java的基本知识点,首先是集合. 一.常用集合类关系图 Collection |___List 有序,可重复 |___ArrayList  底层数据结构是数组,增 ...

  7. [转载]Windows服务编写原理及探讨(4)

    (四)一些问题的讨论 前面几章的内容都是服务的一些通用的编写原理,但里面隐含着一些问题,编写简单的服务时看不出来,但遇到复杂的应用就会出现一些问题,所以本章就是用来分析.解决这些问题的,适用于高级应用 ...

  8. go语言 documentation

    Documentation文档   The Go programming language is an open source project to make programmers more pro ...

  9. mac设置java环境变量, 使用oh-my-zsh

    参考:http://www.mkyong.com/java/how-to-set-java_home-environment-variable-on-mac-os-x/ 如果用bash,修改~/.ba ...

  10. 2、图文讲解.NET CLR是什么

    大家首先要清楚的是,.NET平台与C#不是一回事.这点大家一定要明白,对开发人员来讲他有两个概念.第一,它是C#,VB.net等程序运行的平台.第二,它因为为这些语言提供了丰富的类库(称之为基类库), ...