【例题】【树链剖分】
来源:互联网 发布:复制文件 linux 编辑:程序博客网 时间:2024/05/18 11:46
1、
【USACO 2011 Dec Gold 】种草
时间限制 : 10000 MS 空间限制 : 65536 KB
问题描述
农夫约翰有N块贫瘠的牧场(2 <= N <= 100,000),有N-1条双向道路将这N个牧场连接了起来,每两个牧场间都有且仅有一条路径可相互到达。著名奶牛贝西经常抱怨:为什么连接牧场的道路上没有草可吃呢?
约翰非常喜欢贝西,今天约翰终于决定要在道路上种草了。约翰的种草工作被分成了M(1 <= M <=100,000)步操作。
在每一步中,下列两个事件中的一个会发生:
1.约翰会选择两个牧场,沿着两个牧场间的路径,在路径上的每一条道路上都种植1株牧草;
2.贝西会向约翰提问:在一条指定的道路上,种植了多少株牧草;
请帮助约翰回答贝西的问题。
输入格式
第一行,两个空格间隔的整数N和M
接下来N-1行,每行两个整数x和y,表示牧场x和y之间有道路直接相连
接下来M行,每行描述一步操作:
每行以字母P或Q作为开头,P代表种草操作,Q代表询问操作,接下来两个整数,A_i 和 B_i用于描述该步的操作(1 <= A_i, B_i <= N)。
输出格式
对于每一次询问,输出一行,一个整数,表示询问的答案
样例输入
4 6
1 4
2 4
3 4
P 2 3
P 1 3
Q 3 4
P 1 4
Q 2 4
Q 1 4
样例输出
2
1
2
思路:
树剖步骤:
1、深搜:找重链、找爸爸
2、再深搜:标链祖先、重标id
3、维护加乱搞:拆成多段…
注意:该题将边权值存储在点上,所以每次操作中最后在主链上的操作需要将x++,否则会多算一条,但是可能x++后出现x>y的情况,需特判
#include<cstdio>#include<iostream>using namespace std;const int need=100004;//............................................inline void inc(char &c){ c=getchar(); while(c==' '||c==10) c=getchar();} inline void in_(int &d){ char t=getchar(); while(t<'0'||t>'9') t=getchar(); for(d=0;!(t<'0'||t>'9');t=getchar()) d=(d<<1)+(d<<3)+t-'0';}inline void out_(int x){ if(x>=10) out_(x/10); putchar(x%10+'0');}//............................................int tot;int fi[need],la[need<<1],en[need<<1];inline void add(){ int a,b;in_(a),in_(b); tot++; la[tot]=fi[a],en[tot]=b,fi[a]=tot; tot++; la[tot]=fi[b],en[tot]=a,fi[b]=tot;}//............................................#define ls (s<<1)#define rs ((s<<1)|1)int val[need<<3],aa[need<<3],bb[need<<3],lazy[need<<3];void build(int s,int l,int r){ aa[s]=l,bb[s]=r; if(l==r) return ; build(ls,l,(l+r)>>1),build(rs,(l+r)/2+1,r);}inline void putdown(int s){ int k=lazy[s]; lazy[s]=0; if(aa[s]==bb[s]) return ; val[ls]+=k,val[rs]+=k; lazy[ls]+=k,lazy[rs]+=k;}int x,y;void ins_(int s){ if(aa[s]>y||bb[s]<x) return ; if(x<=aa[s]&&bb[s]<=y) { val[s]+=bb[s]-aa[s]+1; lazy[s]++; return ; } if(lazy[s]) putdown(s); ins_(ls),ins_(rs); val[s]=val[ls]+val[rs];}int ask_(int s){ if(aa[s]>y||bb[s]<x) return 0; if(x<=aa[s]&&bb[s]<=y) return val[s]; if(lazy[s]) putdown(s); return ask_(ls)+ask_(rs);}//............................................int wws[need],fa[need],size[need],dep[need];int idid,id[need],ancestor[need];void find_w(int x){ int t,y,msize=0; size[x]=1; for(t=fi[x];t;t=la[t]) { y=en[t]; if(fa[x]==y) continue; fa[y]=x; dep[y]=dep[x]+1; find_w(y); size[x]+=size[y]; if(size[y]>msize) msize=size[y],wws[x]=y; }}void find_a(int x){ id[x]=++idid; if(wws[x]) find_a((ancestor[wws[x]]=ancestor[x],wws[x])); for(int t=fi[x],y;t;t=la[t]) { y=en[t]; if(y==fa[x]||y==wws[x]) continue; find_a(ancestor[y]=y); }}void tree(){find_w(1),find_a(ancestor[1]=1);}//............................................void change(int u,int v){ while(ancestor[u]!=ancestor[v]) { if(dep[ancestor[u]]<dep[ancestor[v]]) swap(u,v);//深度大的往上 x=id[ancestor[u]],y=id[u]; ins_(1); u=fa[ancestor[u]]; } if(dep[u]>dep[v]) swap(u,v);//深度大的在后 x=id[u]+1,y=id[v]; if(x<=y) ins_(1);}int ask(int u,int v){ int ans=0; while(ancestor[u]!=ancestor[v]) { if(dep[ancestor[u]]<dep[ancestor[v]]) swap(u,v); x=id[ancestor[u]],y=id[u]; ans+=ask_(1); u=fa[ancestor[u]]; } if(dep[u]>dep[v]) swap(u,v); x=id[u]+1,y=id[v]; if(x<=y) ans+=ask_(1); return ans;}//............................................int main(){ int n,m;in_(n),in_(m); for(int i=1;i<n;i++) add(); tree(); build(1,1,n); char c; for(int i=1,a,b;i<=m;i++) { inc(c),in_(a),in_(b); if(c=='P') change(a,b); else out_(ask(a,b)),putchar(10); } }
2、
NKOJ2145【SDOI2011 第1轮 DAY1】染色
时间限制 : 40000 MS 空间限制 : 565536 KB
问题描述
给定一棵有个节点的无根树和m个操作,操作有2类:
1、将节点a到节点b路径上所有点都染成颜色c;
2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”、“222”和“1”。
请你写一个程序依次完成这m个操作。
输入格式
第一行包含2个整数n和m,分别表示节点数和操作数;
第二行包含n个正整数表示n个节点的初始颜色
下面n-1行每行包含两个整数x和y,表示x和y之间有一条无向边。
下面m行每行描述一个操作:
“C a b c”表示这是一个染色操作,把节点a到节点b路径上所有点(包括a和b)都染成颜色;
“Q a b”表示这是一个询问操作,询问节点a到节点b(包括a和b)路径上的颜色段数量。
输出格式
对于每个询问操作,输出一行答案。
样例输入
6 5
2 2 1 2 1 1
1 2
1 3
2 4
2 5
2 6
Q 3 5
C 2 1 1
Q 3 5
C 5 1 2
Q 3 5
样例输出
3
1
2
思路:
维护区间内段数、左端颜色、右端颜色,每次询问若相邻两段相邻颜色相同ans–
#include<cstdio>#include<iostream>using namespace std;const int need=100004;int n,m;int a[need],aaa[need];//............................................inline void inc(char &c){ c=getchar(); while(c==' '||c==10) c=getchar();} inline void in_(int &d){ char t=getchar(); while(t<'0'||t>'9') t=getchar(); for(d=0;!(t<'0'||t>'9');t=getchar()) d=(d<<1)+(d<<3)+t-'0';}inline void out_(int x){ if(x>=10) out_(x/10); putchar(x%10+'0');}//............................................int tot,fi[need],la[need<<1],en[need<<1];inline void add(){ int a,b;in_(a),in_(b); tot++; la[tot]=fi[a],fi[a]=tot,en[tot]=b; tot++; la[tot]=fi[b],fi[b]=tot,en[tot]=a;}//............................................int fa[need],ww[need],si[need],dep[need];int idid,id[need],anc[need];void find_w(int x){ int t,y,msi=0; si[x]=1; for(t=fi[x];t;t=la[t]) { y=en[t]; if(fa[x]==y) continue; fa[y]=x; dep[y]=dep[x]+1; find_w(y); si[x]+=si[y]; if(si[y]>msi) {msi=si[y];ww[x]=y;} }}void find_a(int x){ id[x]=++idid; if(ww[x]) find_a((anc[ww[x]]=anc[x],ww[x])); for(int t=fi[x],y;t;t=la[t]) { y=en[t]; if(y==fa[x]||y==ww[x]) continue; find_a(anc[y]=y); }}void tree(){find_w(1),find_a(anc[1]=1);}//............................................#define ls (s<<1)#define rs ((s<<1)|1)int aa[need<<3],bb[need<<3],as[need<<3],bs[need<<3],tt[need<<3],lazy[need<<3];inline void NBHB(int s){ if(aa[s]==bb[s]) return ; as[s]=as[ls],bs[s]=bs[rs]; tt[s]=tt[ls]+tt[rs]; if(bs[ls]==as[rs]) tt[s]--;}void build(int s,int l,int r){ aa[s]=l,bb[s]=r; if(l==r) { tt[s]=1; as[s]=bs[s]=aaa[l]; return ; } build(ls,l,(l+r)>>1),build(rs,(l+r)/2+1,r); NBHB(s);}inline void putdown(int s){ int k=lazy[s]; lazy[s]=0; if(aa[s]==bb[s]) return; lazy[ls]=lazy[rs]=k; as[ls]=bs[ls]=as[rs]=bs[rs]=k,tt[ls]=tt[rs]=1;}int x,y,d;int getc(int s){ if(aa[s]==bb[s]) return as[s]; if(lazy[s]) return lazy[s];//该区间内所有点颜色都为lazy int mid=(aa[s]+bb[s])/2; if(d<=mid) return getc(ls); else return getc(rs);}void change_(int s){ if(aa[s]>y||bb[s]<x) return ; if(x<=aa[s]&&bb[s]<=y) { tt[s]=1; as[s]=bs[s]=d; if(aa[s]!=bb[s]) lazy[s]=d; return ; } if(lazy[s]!=0) putdown(s); change_(ls),change_(rs); NBHB(s);}int ask_(int s){ if(aa[s]>y||bb[s]<x) return 0; if(x<=aa[s]&&bb[s]<=y) return tt[s]; if(lazy[s]!=0) putdown(s); int mid=(aa[s]+bb[s])>>1; if(y<=mid) return ask_(ls); if(x>mid) return ask_(rs); return ask_(ls)+ask_(rs)-(bs[ls]==as[rs]);}//............................................void change(int u,int v){ while(anc[u]!=anc[v]) { if(dep[anc[v]]>dep[anc[u]]) swap(u,v); x=id[anc[u]],y=id[u]; change_(1); u=fa[anc[u]]; } if(dep[v]>dep[u]) swap(u,v); x=id[v],y=id[u]; change_(1);}int ask(int u,int v){ int ans=0; while(anc[v]!=anc[u]) { if(dep[anc[v]]>dep[anc[u]]) swap(u,v); x=id[anc[u]],y=id[u]; ans+=ask_(1); int c,cf=0; if(fa[anc[u]]) d=id[fa[anc[u]]],cf=getc(1); d=id[anc[u]],c=getc(1); if(c==cf) ans--; u=fa[anc[u]]; } if(dep[u]>dep[v]) swap(u,v); x=id[u],y=id[v]; ans+=ask_(1); return ans; }//............................................int main_(){ int n,m;in_(n),in_(m); for(int i=1;i<=n;i++) in_(a[i]); for(int i=1;i<n;i++) add(); tree(); for(int i=1;i<=n;i++) aaa[id[i]]=a[i]; build(1,1,n); char b; for(int i=1,c,e;i<=m;i++) { inc(b); if(b!='Q') { in_(c),in_(e),in_(d); change(c,e); } else { in_(c),in_(e); out_(ask(c,e)),putchar(10); } } }const int main_stack=16;char my_stack[128<<20];int main(){ __asm__("movl %%esp, (%%eax);\n"::"a"(my_stack):"memory"); __asm__("movl %%eax, %%esp;\n"::"a"(my_stack+sizeof(my_stack)-main_stack):"%esp"); main_(); __asm__("movl (%%eax), %%esp;\n"::"a"(my_stack):"%esp"); return 0;}
- 【例题】【树链剖分】
- 例题
- 例题
- 例题
- 例题
- 面试例题
- C++ 例题
- 面试例题
- 反射例题
- acm例题
- jdbc例题
- C++例题
- C++例题
- C++例题
- 例题分析
- 各种例题
- 例题9.9
- 例题6.3
- 你为什么一定要学Python?
- 实现小说翻页功能
- 2017年大数据的十大发展趋势
- Iphone连不上电脑的解决方法
- OpenGL 入门4
- 【例题】【树链剖分】
- [Qt creator+Linux]安装UUID(libuuid)以及问题undefined reference to uuid generate
- EasyUI学习之DataGird
- Heritrix3.1.1使用教程
- 连接redis报此错误:ERR Client sent AUTH, but no password is set
- 如何选择Python版本2还是3
- ansible入门
- PDO
- linux centos 7安装极点五笔输入法