Codeforces Round #425 (Div. 2) D 树链剖分 + 树状数组维护区间

一看就知道 可以LCA判断做 也可以树链剖分拿头暴力

然而快速读入和线段树维护区间会T70 于是只能LCA?

线段树的常数不小 于是需要另外一种办法来进行区间加减和查询区间和 就是使用树状数组

这个题的代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
#define pb push_back

int n , qn ;
vector<int > q [100050] ;
int top[100050] , fa[100050] , deep[100050] , num[100050] , p[100050] , fp[100050] , son[100050] , pos ;

inline int read()
{
    int x=0;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘)ch=getchar();
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x;
}

///----------

int c[100050] ;
int d[100050] ;
int lowbit(int x) {
    return (x & (-x)) ;
}
void add(int c[] , int x ,int val) {
    while(x <= n+5) {
        c[x] += val ; x += lowbit(x) ;
    }
}
int sum(int c[] , int x) {
    int res = 0 ;
    while(x > 0) {
        res += c[x] ;
        x -= lowbit(x) ;
    }
    return res ;
}
void add(int l , int r , int val) {
    add(c , l , val) ; add(d , l , l * val) ;
    add(c , r+1 , -val ) ; add(d , r + 1 , -val * (r+1)) ;
}
int sum(int x) {
    return (x + 1) * sum(c , x) - sum(d , x) ;
}
int sum(int l , int r) {
    return sum(r) - sum(l - 1) ;
}

///----------

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) {
            dfs1(v , u , d+1) ;
            num[u] += num[v] ;
            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 chang(int u , int v , int val) {
    int f1 = top[u] , f2 = top[v] ;
    int tmp = 0 ;
    while(f1 != f2) {
        if(deep[f1] < deep[f2]) {
            swap(f1 , f2) ; swap(u , v) ;
        }
        add(p[f1] , p[u] , val) ;
        u = fa[f1] ;
        f1 = top[u] ;
    }
    if(deep[u] > deep[v]) swap(u , v) ;
    add(p[u] , p[v] , val) ;
}
int que(int u , int v) {
    int f1 = top[u] , f2 = top[v] ;
    int tmp = 0 ;
    while(f1 != f2) {
        if(deep[f1] < deep[f2]) {
            swap(f1 , f2) ; swap(u , v) ;
        }
        tmp += sum(p[f1] , p[u]) ;
        u = fa[f1] ;
        f1 = top[u] ;
    }
    if(deep[u] > deep[v]) swap(u , v) ;
    tmp += sum(p[u] , p[v]) ;
    return tmp ;
}

int main () {
    n = read() ;
    qn = read() ;
    for(int i = 2 ; i <= n ; i ++ ) {
        int z ; z = read() ;
        q[z].pb(i) ; q[i].pb(z) ;
    }
    memset(son , -1 , sizeof(son)) ;
    pos = 1 ;
    dfs1(1,0,0) ;
    getpos(1 , 1) ;
    while(qn -- ) {
        int aa , bb , cc ;
        aa = read() ; bb = read() ; cc = read() ;

        int ans = 0 ;

        chang(aa,bb,1) ;
        ans = max(que(bb,cc) , ans) ;
        chang(aa,bb,-1) ;

        chang(aa,cc,1) ;
        ans = max(que(bb,cc) , ans) ;
        chang(aa,cc,-1) ;

        chang(aa,bb,1) ;
        ans = max(que(aa,cc) , ans) ;
        chang(aa,bb,-1) ;

        printf("%d\n" , ans) ;
    }
}

其中的树状数组 拿两个数组来分别维护 具体代码

int c[100050] ;
int d[100050] ;
int lowbit(int x) {
    return (x & (-x)) ;
}
void add(int c[] , int x ,int val) {
    while(x <= n+5) {
        c[x] += val ; x += lowbit(x) ;
    }
}
int sum(int c[] , int x) {
    int res = 0 ;
    while(x > 0) {
        res += c[x] ;
        x -= lowbit(x) ;
    }
    return res ;
}
void add(int l , int r , int val) {
    add(c , l , val) ; add(d , l , l * val) ;
    add(c , r+1 , -val ) ; add(d , r + 1 , -val * (r+1)) ;
}
int sum(int x) {
    return (x + 1) * sum(c , x) - sum(d , x) ;
}
int sum(int l , int r) {
    return sum(r) - sum(l - 1) ;
}

树状数组天下无敌TAT 于是又上网学习了新姿势 我有姿势我自豪

树状数组单点修改 + 区间查询max

int num[5000] ;
int c[5000] ;
int n ;
int lowbit(int x)
{
    return x & (-x);
}
void updata(int x)
{
    int lx, i;
    while (x <= n)
    {
        c[x] = num[x];
        lx = lowbit(x);
        for (i=1; i<lx; i<<=1)
            c[x] = max(c[x], c[x-i]);
        x += lowbit(x);
    }
}
int query(int L, int R)
{
    int ans = 0;
    while (R >= L)
    {
        ans = max(num[R], ans);
        R --;
        for (; R-lowbit(R) >= L; R -= lowbit(R))
            ans = max(c[R], ans);
    }
    return ans;
}

以及这里还有...矩形增减 + 矩形查询和的黑科技。。。

http://blog.csdn.net/lawrence_jang/article/details/8054173

时间: 2024-10-10 09:40:23

Codeforces Round #425 (Div. 2) D 树链剖分 + 树状数组维护区间的相关文章

poj 2763 树链剖分(单点更新,区间求值)

http://poj.org/problem?id=2763 Description After their royal wedding, Jiajia and Wind hid away in XX Village, to enjoy their ordinary happy life. People in XX Village lived in beautiful huts. There are some pairs of huts connected by bidirectional ro

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

题目链接:hdu 3966 Aragorn's Story 题目大意:给定一个棵树,然后三种操作 Q x:查询节点x的值 I x y w:节点x到y这条路径上所有节点的值增加w D x y w:节点x到y这条路径上所有节点的值减少w 解题思路:树链剖分,用树状数组维护每个节点的值. #pragma comment(linker, "/STACK:1024000000,1024000000") #include <cstdio> #include <cstring>

2018中国大学生程序设计竞赛 - 网络选拔赛 1010 YJJ&#39;s Salesman 【离散化+树状数组维护区间最大值】

题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6447 YJJ's Salesman Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 919    Accepted Submission(s): 290 Problem Description YJJ is a salesman who h

Codeforces Round #425 (Div. 2) Problem D Misha, Grisha and Underground (Codeforces 832D) - 树链剖分 - 树状数组

Misha and Grisha are funny boys, so they like to use new underground. The underground has n stations connected with n - 1 routes so that each route connects two stations, and it is possible to reach every station from any other. The boys decided to h

Codeforces Round #426 (Div. 2) D. The Bakery(线段树维护dp)

题目链接: Codeforces Round #426 (Div. 2) D. The Bakery 题意: 给你n个数,划分为k段,每段的价值为这一段不同的数的个数,问如何划分,使得价值最大. 题解: 考虑dp[i][j]表示划分为前j个数划分为i段的最大价值,那么这就是一个n*n*k的dp, 考虑转移方程dp[i][j]=max{dp[i][k]+val[k+1][j]},我们用线段树去维护这个max,线段树上每个节点维护的值是dp[i][k]+val[k+1][j],对于每加进来的一个数a

Codeforces Round #424 (Div. 2) E. Cards Sorting(线段树)

题目链接:Codeforces Round #424 (Div. 2) E. Cards Sorting 题意: 将n个数放进一个队列,每次检查队首,看看是不是队列中最小的数,如果是就扔掉,如果不是就放到队尾. 这样直到队列为空,为需要操作多少次. 题解: 考虑用两个指针模拟,最开始now指针指向第一个数,然后nxt指针指向下一个将要被删除的数. 然后我们要算出这里需要移动多少步,然后删掉这个数,一直重复操作,直到将全部的数删完. nxt指针可以用set来维护,now指针可以用并查集来维护. 计

Playrix Codescapes Cup (Codeforces Round #413, rated, Div. 1 + Div. 2) C. Fountains 【树状数组维护区间最大值】

题目传送门:http://codeforces.com/contest/799/problem/C C. Fountains time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output Arkady plays Gardenscapes a lot. Arkady wants to build two new fountains. The

bzoj1146整体二分+树链剖分+树状数组

其实也没啥好说的 用树状数组可以O(logn)的查询 套一层整体二分就可以做到O(nlngn) 最后用树链剖分让序列上树 1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 inline int read() 7 { 8 int x=0,f=1,ch=getchar(); 9 while(ch<

树链剖分 树的统计

/*题目描述 Description一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w. 我们将以下面的形式来要求你对这棵树完成一些操作: I.                    CHANGE u t : 把结点u的权值改为tII.                 QMAX u v: 询问从点u到点v的路径上的节点的最大权值III.               QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身 输入描述 In