树链剖分是一个很固定的套路 一般用来解决树上两点之间的路径更改与查询

思想是将一棵树分成不想交的几条链 并且由于dfs的顺序性 给每条链上的点或边标的号必定是连着的 那么每两个点之间的路径都可以拆成几条链 那么就是对一群区间进行更改 这时候基本是用线段树进行logn的操作

做了三道基础题 都属于比较好想的 也就是线段树比较麻烦 需要写相当长一段时间...

HDU 3966

给出一棵树的连接状况和边的大小 每次可以对a-b的路径的边的权值取反 或者改变指定边的值 或者求a-b路径的最大值

每次取反 最大值就是原最小值*-1 这样维护下去就好

POJ 3237

给出一棵树的连接状况和点的初始值 每次对a-b的路径上的点权进行加减 询问单点大小

树状数组会超时 只能用线段树...需要注意的是点权和边权在树链剖分的时候会有一点不同 例如 u == v  是否return 等

HYSBZ 2243

总觉得以前见过的样子..

给出一棵树的连接状况和边的初始颜色 每次可以对a-b的路径上的边进行更改颜色

最后求a-b路径上的颜色段个数

可以由线段树的本质来看 每个区间的两个子区间 都是从这个线段树一分为2

那么 一个区间内有多少个颜色段数 就等于他的两个子区间的颜色加起来 如果子区间相邻的点颜色相同 那么久减去一

可以在tr数组中记录这个区间的mlmr 即中点两边的点的颜色

每次记录下来前一条链的尾颜色 和当前链的头颜色进行对比 如果相同 就减一

最后当f1 == f2的时候 要同时对比ys1和ys2

交换uv的时候 ys1和ys2也需要交换

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<map>
#include<iostream>
#include<string>
#include<vector>
using namespace std;
#define L long long vector<int >q[100050]; int p[100050];
int fp[100050];
int fa[100050];
int son[100050];
int deep[100050];
int top[100050];
int num[100050];
int n , m , pp;
int pos;
void init(){
for(int i= 1;i<=n;i++){
q[i].clear();
}
memset(son , -1 ,sizeof(son));
pos = 0;
}
int in[100050];
void dfs(int u , int pre , int d){
deep[u] = d;
fa[u] = pre;
num[u] = 1;
for(int i = 0;i<q[u].size();i++){
int v = q[u][i];
if(v != pre){
dfs(v,u,d+1);
num[u] += num[v];
if(son[u] == -1 || num[v] > num[son[u]]){
son[u] = v;
}
}
}
} void dfs2(int u , int zx){
top[u] = zx;
p[u] = pos ++;
fp[pos - 1] = u;
if(son[u] == -1){
return ;
}
dfs2(son[u],zx);
for(int i = 0;i<q[u].size();i++){
int v = q[u][i];
if(v != fa[u] && v != son[u]){
dfs2(v,v);
}
}
}
/**
线段树 1求一个区间内有多少个颜色 2求一个点的颜色
2直接写查询
1记录这个区间的 中间的两个的颜色
*/
struct node{
int l,r;
int ml;
int mr;
int ll ;
int rr ;
int mark;
int d;
}tr[100050 * 4];
void pushup(int i){
if(tr[i].l == tr[i].r)
return ;
tr[i].ll = tr[i<<1].ll;
tr[i].rr = tr[(i<<1)|1].rr ;
tr[i].ml = tr[i<<1].rr;
tr[i].mr = tr[(i<<1)|1].ll ;
tr[i].d = tr[i<<1].d + tr[(i<<1)|1].d ;
if(tr[i<<1].rr == tr[(i<<1)|1].ll)
tr[i].d -- ;
}
void pushdown(int i){
if(tr[i].l == tr[i].r)return ;
if(tr[i].mark != 0){
tr[i<<1].mark = tr[(i<<1)|1].mark = tr[i].mark ;
tr[i<<1].ll = tr[i<<1].rr = tr[i<<1].ml = tr[i<<1].mr = tr[i].mark;
tr[(i<<1)|1].ll = tr[(i<<1)|1].rr = tr[(i<<1)|1].ml = tr[(i<<1)|1].mr = tr[i].mark;
tr[i<<1].d= tr[(i<<1)|1].d = 1;
tr[i].mark = 0;
}
}
void crea(int i , int l , int r){
tr[i].l = l ;
tr[i].r = r;
tr[i].ml = tr[i].mr = tr[i].ll = tr[i].rr = tr[i].mark = 0;
if(l == r){
tr[i].ml = tr[i].mr = tr[i].ll = tr[i].rr = in[fp[l]];
tr[i].d = 1;
return ;
}
int mid = (l + r )/ 2;
crea(i<<1,l,mid);
crea((i<<1)|1,mid+1,r);
pushup(i);
}
int res ;
void upda(int i ,int l ,int r , int c){
if(tr[i].l ==l && r == tr[i].r){
tr[i].ml = tr[i].mr = tr[i].ll = tr[i].rr = tr[i].mark = c;
tr[i].d = 1;
return ;
}
pushdown(i);
int mid = (tr[i].l + tr[i].r) / 2;
if(r <= mid){
upda(i<<1,l,r,c);
}
else if(l > mid){
upda((i<<1)|1,l,r,c);
}
else {
upda(i<<1,l,mid,c);
upda((i<<1)|1,mid+1,r,c);
}
pushup(i);
}
int query(int i ,int l ,int r){
if(tr[i].l == l && r == tr[i].r){
return tr[i].d;
}
pushdown(i);
int mid = (tr[i].l + tr[i].r) / 2;
if(r <= mid){
return query(i<<1,l,r);
}
else if(l > mid){
return query((i<<1)|1,l,r);
}
else {
if(tr[i].ml == tr[i].mr){
return query(i<<1,l,mid) + query((i<<1)|1,mid+1,r) - 1;
}
else {
return query(i<<1,l,mid) + query((i<<1)|1,mid+1,r);
}
}
}
int qu2(int i ,int pos){
if(tr[i].l == tr[i].r){
return tr[i].ml ;
}
pushdown(i);
int mid = (tr[i].l + tr[i].r) / 2;
if(pos <= mid){
return qu2(i<<1,pos);
}
else {
return qu2((i<<1)|1,pos);
}
}
void did(int u , int v, int c){
int f1 = top[u];
int f2 = top[v];
while(f1 != f2){
if(deep[f1] < deep[f2]){
swap(f1,f2);
swap(u,v);
}
upda(1,p[f1],p[u],c);
u = fa[f1];
f1 = top[u];
}
if(deep[u] < deep[v]){
swap(u,v);
}
upda(1,p[v],p[u],c);
}
int slpf(int u, int v){
int ans = 0;
int f1 = top[u];
int f2 = top[v];
int ys1 = -1;
int ys2 = -1;
while(f1 != f2){
if(deep[f1] < deep[f2]){
swap(u,v);
swap(f1,f2);
swap(ys1,ys2);
}
ans += query(1,p[f1],p[u]);
if(ys1 == qu2(1,p[u])){
ans -- ;
}
ys1 = qu2(1,p[f1]);
u = fa[f1];
f1 = top[u];
//printf("%d\n",ans);
}
if(deep[u] < deep[v]){
swap(u,v);
swap(ys1,ys2); /// --------
}
ans += query(1,p[v],p[u]);
if(ys1==qu2(1,p[u])){
ans -- ;
}
if(ys2==qu2(1,p[v])){
ans -- ;
} return ans ;
}
int main(){
//freopen("in.cpp","r",stdin);
while(scanf("%d%d",&n,&pp)!=EOF){
init();
for(int i=1;i<=n;i++){
scanf("%d",&in[i]);
} for(int i=1;i<=n-1;i++){
int u , v;
scanf("%d%d",&u,&v);
q[u].push_back(v);
q[v].push_back(u);
}
dfs(1,0,0);
dfs2(1,1);
crea(1,0,pos-1);
for(int i=1;i<=pp;i++){
char s[20];
scanf("%s",s);
if(s[0] == 'C'){
int u , v, c;
scanf("%d%d%d",&u,&v,&c);
did(u,v,c);
}
else {
int u,v;
scanf("%d%d",&u,&v);
printf("%d\n",slpf(u,v));
}
}
}
}

感觉树链剖分的主要难点在于线段树的构造...

2017.3.18 很久后又做了一道树链剖分 当然LCA也可做

询问树上路径可否组成三角形 利用斐波那契的思想 由于每一条路径的len<=1e9 所以当路径的条数超过64左右的时候肯定是可以的

当小于64的时候拿出来直接暴力就可以了

树链剖分中 u与top[u] 是连着的 在这里 用a[u]存放 u到fa[u] 的边的权值

所以top[u] -> fa[top[u]] 其实是不在 u -> f1 这条链上的 但是之后会发生 u = fa[f1] 直接翻到这条边上去

所以如果top[u] -> fa[top[u]] 这个边不在路径中 是会直接发生 f1 == f2 的

所以这样记录是不会出现什么问题的 QAQ

好久不写 好麻烦...Orz

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<map>
#include<iostream>
#include<string>
#include<vector>
#include<queue>
using namespace std;
#define L long long const int maxn = 100050 ; int top[maxn] , fa[maxn] , deep[maxn] , num[maxn] , p[maxn] , fp[maxn] , son[maxn] ;
int pos ;
int n ;
int a[maxn] ;
bool can ; vector<int > q[maxn] ;
vector<int > w[maxn] ; vector<int > tmp ; void init() {
pos = 1 ;
memset(son , -1 , sizeof(son)) ;
for(int i = 1 ; i <= n ; i ++ ) q[i].clear() ;
for(int i = 1 ; i <= n ; i ++ ) w[i].clear() ;
} void dfs1(int u , int pre , int d) {
deep[u] = d ;
fa[u] = pre ;
num[u] = 1 ;
for(int i = 0 ; i < q[u].size() ; i ++ ){
int v = q[u][i] ;
if(v != pre) {
a[v] = w[u][i] ;
dfs1(v,u,d+1);
if(son[u] == -1 || num[v] > num[son[u]]) {
son[u] = v ;
}
}
}
} void getpos(int u , int sp) {
top[u] = sp ;
p[u] = pos ++ ;
fp[p[u]] = u ;
if(son[u] == -1) return ;
getpos(son[u] , sp) ;
for(int i = 0 ; i < q[u].size() ; i ++ ){
int v = q[u][i];
if(v != son[u] && v != fa[u]) {
getpos(v,v) ;
}
}
} void slpf(int u ,int v ) {
int f1 = top[u] , f2 = top[v] ;
int sl = 0 ;
while(f1 != f2) {
if(deep[f1] < deep[f2]) {
swap(f1 , f2) ;
swap(u , v) ;
}
sl += ( p[u] - p[f1] + 1) ;
if(sl > 65) {
can = false ;
return ;
}
else {
for(int i = p[f1] ; i <= p[u] ; i ++ ){
tmp.push_back(a[fp[i]]) ;
}
}
u = fa[f1] ;
f1 = top[u] ;
}
if(u == v) return ;
if(deep[u] > deep[v]) swap(u , v) ;
sl += (p[v] - p[son[u]] + 1) ;
if(sl > 65) {
can = false ;
return ;
}
for(int i = p[son[u]] ; i <= p[v] ; i ++ ){
tmp.push_back(a[fp[i]]) ;
}
} int main(){
while(scanf("%d" , &n) != EOF) {
init() ;
for(int i = 1 ; i < n ; i ++ ){
int u , v , c ;
scanf("%d%d%d",&u,&v,&c) ;
q[u].push_back(v) ;
q[v].push_back(u) ;
w[u].push_back(c) ;
w[v].push_back(c) ;
}
dfs1(1,0,0) ;
getpos(1,1) ;
a[1] = -9999999 ;
int query ;
scanf("%d",&query) ;
for(int i = 1 ; i <= query ; i ++ ){
int u , v ;
scanf("%d%d",&u,&v) ;
tmp.clear() ;
can = true ;
slpf(u , v) ;
if(can == false) {
printf("Yes\n") ;
}
else {
sort(tmp.begin() , tmp.end()) ;
bool ok = false ;
if(tmp.size() < 3) { }
else {
for(int j = 2 ; j < tmp.size() ; j ++ ){
int aa = tmp[j-2] ;
int bb = tmp[j-1] ;
int cc = tmp[j] ;
if(aa + bb > cc) {
ok=true;
break ;
}
}
}
if(ok)printf("Yes\n");
else printf("No\n") ;
}
}
}
}

  

HDU 3966 & POJ 3237 & HYSBZ 2243 树链剖分的更多相关文章

  1. HDU 3966 & POJ 3237 & HYSBZ 2243 & HRBUST 2064 树链剖分

    树链剖分是一个很固定的套路 一般用来解决树上两点之间的路径更改与查询 思想是将一棵树分成不想交的几条链 并且由于dfs的顺序性 给每条链上的点或边标的号必定是连着的 那么每两个点之间的路径都可以拆成几 ...

  2. hdu 3966 Aragorn&#39;s Story(树链剖分+树状数组)

    pid=3966" target="_blank" style="">题目链接:hdu 3966 Aragorn's Story 题目大意:给定 ...

  3. HDU - 3966 Aragorn's Story(树链剖分入门+线段树)

    HDU - 3966 Aragorn's Story Time Limit: 3000MS   Memory Limit: 32768KB   64bit IO Format: %I64d & ...

  4. hdu 3966 Aragorn's Story(树链剖分+树状数组/线段树)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3966 题意: 给出一棵树,并给定各个点权的值,然后有3种操作: I C1 C2 K: 把C1与C2的路 ...

  5. hdu 3966 Aragorn's Story(树链剖分+区间修改+单点查询)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3966 题意:给一棵树,并给定各个点权的值,然后有3种操作: I C1 C2 K: 把C1与C2的路径上 ...

  6. poj 3237 Tree(树链剖分,线段树)

    Tree Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 7268   Accepted: 1969 Description ...

  7. POJ 3237 Tree (树链剖分)

    Tree Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 2825   Accepted: 769 Description ...

  8. HDU 3966 Aragorn's Story (树链剖分+树状数组)

    Aragorn's Story Time Limit: 10000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...

  9. HDU 3966 Aragorn's Story (简单树链剖分)

    题意:给一棵树,并给定各个点权的值,然后有3种操作: I C1 C2 K: 把C1与C2的路径上的所有点权值加上K D C1 C2 K:把C1与C2的路径上的所有点权值减去K Q C:查询节点编号为C ...

随机推荐

  1. wampServer图标为橙色无法启动原因之一

    前段时间,自己在本地做了一个WordPress的网站,利用wampserver配置的,后来突然无法启动了. 经过仔细查找发现是因为之前装了sql server,导致wampServer无法启动,那么怎 ...

  2. 28. 字符串的排列之第1篇[StringPermutation]

    [题目] 输入一个字符串,打印出该字符串中字符的所有排列.例如输入字符串abc,则输出由字符a.b.c所能排列出来的所有字符串abc.acb.bac.bca.cab和cba. [分析] 这是一道很好的 ...

  3. asp.net中membership使用oracle数据库(二)

    需要安装的东西都准备好了,继续生成后台表.过程.函数.触发器等.ps/sql中 @@E:\oracle\product\11.2.0\client_1\ASP.NET\SQL\InstallAllOr ...

  4. jquery ashx交互 返回list 循环json输入信息

    html代码:触发按钮 <input type="button" id="search" value="查询" /> ashx代 ...

  5. 设置app的启动图

    Step1 1.点击Image.xcassets 进入图片管理,然后右击,弹出"New Launch Image" 2.如图,右侧的勾选可以让你选择是否要对ipad,横屏,竖屏,以 ...

  6. Power BI for Office 365(六)Power Map简介

    如果说Power BI中最给力的功能是什么,我觉得是Power Map.Power Map第一次是出现在SQL Server 2014的新特性里被提及,前身就是GeoFlow.在Power Map下可 ...

  7. 使用FTP FtpWebRequest UsePassive 属性实现主动上传

    类型:System::Boolean如果客户端应用程序的数据传输过程侦听数据端口上的连接,则为 false:如果客户端应在数据端口上启动连接,则为 true. 默认值为 true. UsePassiv ...

  8. Struts2 动态方法调用

    01.Struts 2基本结构 使用Struts2框架实现用登录的功能,使用struts2标签和ognl表达式简化了试图的开发,并且利用struts2提供的特性对输入的数据进行验证,以及访问Servl ...

  9. HTML5中的对象的拖拽

    拖拽: draggable="true"页面上就能实现拖拽事件: ondragstart 拖拽开始事件 ondrag 拖拽中 ondragend 拖拽结束事件 投放区事件: ond ...

  10. CentOS 6.5 下安装 Redis 2.8.7

    wget http://download.redis.io/redis-stable.tar.gz tar xvzf redis-stable.tar.gz cd redis-stable make ...