FZOJ 2176 easy problem ( 树链剖分 )

题目链接~~>

做题感悟:感觉做多了树链剖分的题目,有很多是树链剖分 + 想法。。

解题思路:

这题非常明显的一点就是 k 非常小,那就是告诉你能够从 k 入手,如何入手呢 ? 观察能够发现无非最多是 k 类点 ,0 ~ k-1 ,分别表示与根的距离模 k .这样就能够把点分类加权值,可是每一个线段树里存的还是全部元素,查询的时候相应查找。

代码:

#include<iostream>
#include<sstream>
#include<map>
#include<cmath>
#include<fstream>
#include<queue>
#include<vector>
#include<sstream>
#include<cstring>
#include<cstdio>
#include<stack>
#include<bitset>
#include<ctime>
#include<string>
#include<cctype>
#include<iomanip>
#include<algorithm>
using namespace std  ;
#define INT long long int
#define L(x)  (x * 2)
#define R(x)  (x * 2 + 1)
const int INF = 0x3f3f3f3f ;
const double esp = 0.00000000001 ;
const double PI = acos(-1.0) ;
const INT mod = 1234567891 ;
const int MY = (1<<5) + 5 ;
const int MX = 50000 + 5 ;
const int S = 20 ;
int n ,m ,k ,num ,idx ;
int head[MX] ,top[MX] ,siz[MX] ,son[MX] ,dep[MX] ,ti[MX] ,to[MX] ,father[MX] ;
struct Edge
{
    int v ,next ;
}E[MX*2] ;
void addedge(int u ,int v)
{
    E[num].v = v ; E[num].next = head[u] ;head[u] = num++ ;
    E[num].v = u ; E[num].next = head[v] ;head[v] = num++ ;
}
void dfs_find(int u ,int fa)
{
    dep[u] = dep[fa] + 1 ;
    siz[u] = 1 ;
    son[u] = 0 ;
    father[u] = fa ;
    for(int i = head[u] ;i != -1 ;i = E[i].next)
    {
        int v = E[i].v ;
        if(v == fa)  continue ;
        dfs_find(v ,u) ;
        siz[u] += siz[v] ;
        if(siz[son[u]] < siz[v])  son[u] = v ;
    }
}
void dfs_time(int u ,int fa)
{
    top[u] = fa ;
    ti[u] = idx++ ;
    if(son[u])   dfs_time(son[u] ,top[u]) ;
    for(int i = head[u] ;i != -1 ;i = E[i].next)
    {
        int v = E[i].v ;
        if(v == son[u] || v == father[u])  continue ;
        dfs_time(v ,v) ;
    }
    to[u] = idx-1 ;
}
struct node
{
    int le ,rt ,add ,c ;
}T[6][MX*4] ;
void push_down(int cnt ,int i)
{
    int& temp = T[cnt][i].add ;
    if(temp)
    {
        T[cnt][L(i)].add += temp ;
        T[cnt][L(i)].c += temp ;
        T[cnt][R(i)].add += temp ;
        T[cnt][R(i)].c += temp ;
        temp = 0 ;
    }
}
void build(int cnt ,int i ,int le ,int rt)
{
    T[cnt][i].le = le ; T[cnt][i].rt = rt ;
    T[cnt][i].add = T[cnt][i].c = 0 ;
    if(le == rt)  return ;
    int Mid = (le + rt)>>1 ;
    build(cnt ,L(i) ,le ,Mid) ;
    build(cnt ,R(i) ,Mid+1 ,rt) ;
}
void section(int cnt ,int i ,int le ,int rt ,int val)
{
    if(T[cnt][i].le == le && T[cnt][i].rt == rt)
    {
        T[cnt][i].add += val ;
        T[cnt][i].c += val ;
        return ;
    }
    push_down(cnt ,i) ;
    int Mid = (T[cnt][i].le + T[cnt][i].rt)>>1 ;
    if(le > Mid)   section(cnt ,R(i) ,le ,rt ,val) ;
    else if(rt <= Mid)   section(cnt ,L(i) ,le ,rt ,val) ;
    else
    {
        section(cnt ,L(i) ,le ,Mid ,val) ;
        section(cnt ,R(i) ,Mid+1 ,rt ,val) ;
    }
}
int Query(int cnt ,int i ,int pos)
{
    if(T[cnt][i].le == T[cnt][i].rt)
        return  T[cnt][i].c ;
    push_down(cnt ,i) ;
    int Mid = (T[cnt][i].le + T[cnt][i].rt)>>1 ;
    if(pos <= Mid)    return Query(cnt ,L(i) ,pos) ; // 在左边
    else    return Query(cnt ,R(i) ,pos) ;
}
int main()
{
    //freopen("input.txt" ,"r" ,stdin) ;
    int Tx ,u ,v ,cnt ,x ,dt ,pos ,val ,cse = 1 ;
    scanf("%d" ,&Tx) ;
    while(Tx--)
    {
        scanf("%d%d%d" ,&n ,&m ,&k) ;
        num = 0 ;
        memset(head ,-1 ,sizeof(head)) ;
        for(int i = 1 ;i < n ; ++i)
        {
            scanf("%d%d" ,&u ,&v) ;
            addedge(u ,v) ;
        }
        dep[1] = siz[0] = 0 ;
        dfs_find(1 ,1) ;
        idx = 1 ;
        dfs_time(1 ,1) ;
        for(int i = 0 ;i < k ; ++i)
           build(i ,1 ,1 ,n) ;
        printf("Case#%d:\n" ,cse++) ;
        for(int i = 0 ;i < m ; ++i)
        {
            scanf("%d%d" ,&pos ,&x) ;
            if(pos == 1)
            {
                scanf("%d" ,&val) ;
                for(int j = 0 ;j < k ; ++j)
                {
                    dt = (dep[x] - 1)%k ;  // 根所在的集合
                    cnt = (dt + j)%k ;
                    section(cnt ,1 ,ti[x] ,to[x] ,(j+1)*val) ;
                }
            }
            else
                printf("%d\n" ,Query((dep[x]-1)%k ,1 ,ti[x])) ;
        }
    }
    return 0 ;
}
时间: 2024-10-21 22:00:10

FZOJ 2176 easy problem ( 树链剖分 )的相关文章

HDU 5296 Annoying Problem 树链剖分 LCA 倍增法

HDU 5296 Annoying Problem 题目链接:hdu 5296 题意:在一棵给定的具有边权的树,一个节点的集合S(初始为空),给定Q个操作,每个操作增加或删除S中的一个点,每个操作之后输出使集合S中所有点联通的最小子树的边权和. 思路:最小子树上的节点的充要条件: 节点为(S集合中所有点的LCA)的子节点: 节点有一个子孙为S集合中的点. 那么我们给每个节点都开一个标记数组,初始为零,每加入一个节点,就把从这个节点到根节点路径上的点的值都+1,反之-1,这样通过对每个单节点值的查

HDU 4729 An Easy Problem for Elfness(树链剖分边权+二分)

题意 链接:https://cn.vjudge.net/problem/HDU-4729 给你n个点,然你求两个点s和t之间的最大流.而且你有一定的钱k,可以进行两种操作 1.在任意连个点之间建立一个单位1的流,费用a 2.将原先的流扩大1个单位,费用b 思路 题目已经说了是一棵树,那么树上两点的最大流就是两点路径上的最小值.其实两种操作各一次对最大流的贡献是相等的.我们分类讨论: 如果a<=b,直接算第一种方案即可,直接给s.t连一条边,对答案的贡献是k/a. 如果a>b,分两种情况.如果k

FZU2176---easy problem (树链剖分)

http://acm.fzu.edu.cn/problem.php?pid=2176 Problem 2176 easy problem Accept: 9    Submit: 32Time Limit: 2000 mSec    Memory Limit : 32768 KB  Problem Description 给定一棵n个节点以1为根的树,初始每个节点的值为0,现在我们要在树上进行一些操作,操作有两种类型. 1 x val 表示对以x为根的子树的每个点进行加权操作(我们定义每个节点的

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

(中等) HDU 5293 Tree chain problem,树链剖分+树形DP。

Problem Description Coco has a tree, whose vertices are conveniently labeled by 1,2,…,n.There are m chain on the tree, Each chain has a certain weight. Coco would like to pick out some chains any two of which do not share common vertices.Find out the

HDOJ 5293 Tree chain problem LCA+树链剖分+树形DP

[题意] 给定一颗树上的几条链和每条链的权值,求能取出的不含有公共节点的链的最大权值.... [解] 预处理每条链的lca 树形DP, d[i]表示取到这个节点时可以得到的最大值 , sum[i]=sigma( d[k] | k 是i的子节点) 如果不取i  d[i]=sum[i] 如果取i , e是lca为i的链则 d[i]=max(d[i],e的权值+sigma(sum[k])-sigma(d[k]))  k为树链上的点 可以用树链剖分+树装数组在nlogn的时间复杂度内求链上的值 Tree

【bzoj4999】This Problem Is Too Simple! 树链剖分+动态开点线段树

题目描述 给您一颗树,每个节点有个初始值. 现在支持以下两种操作: 1. C i x(0<=x<2^31) 表示将i节点的值改为x. 2. Q i j x(0<=x<2^31) 表示询问i节点到j节点的路径上有多少个值为x的节点. 输入 第一行有两个整数N,Q(1 ≤N≤ 100,000:1 ≤Q≤ 200,000),分别表示节点个数和操作个数. 下面一行N个整数,表示初始时每个节点的初始值. 接下来N-1行,每行两个整数x,y,表示x节点与y节点之间有边直接相连(描述一颗树).

hdu4729 树链剖分+二分

An Easy Problem for Elfness Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)Total Submission(s): 1235    Accepted Submission(s): 257 Problem Description Pfctgeorge is totally a tall rich and handsome guy. He plans t

树链剖分简(单)介(绍)

树链剖分可以算是一种数据结构(一大堆数组,按照这个意思,主席树就是一大堆线段树).将一棵树分割成许多条连续的树链,方便完成一下问题: 单点修改(dfs序可以完成) 求LCA(各种乱搞也可以) 树链修改(修改任意树上两点之间的唯一路径) 树链查询 (各种操作)  前两个内容可以用其他方式解决,但是下面两种操作倍增.st表,dfs序就很难解决(解决当然可以解决,只是耗时长点而已).下面开始步入正题. 树链剖分的主要目的是分割树,使它成一条链,然后交给其他数据结构(如线段树,Splay)来进行维护.常