cf#381D 树状数组+二分

http://codeforces.com/contest/740/problem/D

之前就学过一些树状数组,但仅限于单点修改,区间查询。。
树状数组到底是怎么回事 搞懂树状数组原理
大神说的很清楚辣,简单的说就是开一个新数组利用二进制记录原数组某几个的和,最后实现快速的求任意区间的和,突然想起当年图卡的1248码。
当知道了树状数组是求区间和很快很方便之后,对于区间修改,单点询问i,可以也这么用,求一个数组的前I项和就是i改变了多少,如何操作呢?
假如在ai~aj上+5
那c【i】+= 5 c【j+1】-= 5 是不是就满足了i到j的所有数都+5,因为每个数字在这个区间中的都被+了一次5

记住树状数组是快速求和其实就好想多了

这道题呢outputstandard output
Alyona has a tree with n vertices. The root of the tree is the vertex 1. In each vertex Alyona wrote an positive integer, in the vertex i she wrote ai. Moreover, the girl wrote a positive integer to every edge of the tree (possibly, different integers on different edges).
Let’s define dist(v, u) as the sum of the integers written on the edges of the simple path from v to u.
The vertex v controls the vertex u (v ≠ u) if and only if u is in the subtree of v and dist(v, u) ≤ au.
Alyona wants to settle in some vertex. In order to do this, she wants to know for each vertex v what is the number of vertices u such that v controls u.

题意:

给你一棵树,u是v的祖先且uv距离小于等于a【v】时代表着u能控制v,问每个u能控制多少v

tip:

这个题用lca可以,我还没做,先说说树状数组的做法,首先预处理出来每个点和root的距离,再dfs一遍记录下来这条链上高度h的点是哪个,树状数组的下标也是h,二分高度,找到距离可以的最下面的点,这个时候从这个点的father到找到的这个点都是可以控制当前v的(区间修改),每次dfs结束所有孩子时统计这个点控制了多少点(单点询问),然后这个h的树状数组位置要归零,至于怎么归零,相当于c【h】-= 和,c【h+1】+=和。这个题就结束了,记得注意long long

#include 
#include 
#include 
using namespace std;
#define LL long long
const int maxn = 2*1e5+100;
int n,root,tot,first[maxn],arr[maxn],ans[maxn];
LL dist[maxn],treea[maxn],au[maxn];
struct node{
    int v,dis,next;
}edges[maxn];

void add(int u,int v,int d){
    edges[tot].v = v;edges[tot].dis = d;
    edges[tot].next = first[u];first[u] = tot++;
}

void init(){
    tot = 0;
    memset(first,-1,sizeof(first));
    for(int i = 1; i <= n ;i++)
        scanf("%I64d",&au[i]);
    for(int i = 2 ; i <= n;i++){
        int s,d;
        scanf("%d%d",&s,&d);
        add(s,i,d);
    }
    dist[1] = 0;
}

int lowerbound(int size,LL key,int pos){
    int l =1 ,r = size;
    while(l < r){
        int m = l+(r-l)/2;
        if(treea[m] < key){
            l = m+1;
            pos = l;
        }
        else{
            r = m;
            pos = r;
        }
    }
   // printf("pos = %d\n",pos);
    return pos;
}

int lowbit(int x){
    return x&(-x);
}
void add(int pos,int num){
    for(int i = pos ; i < maxn ;i += lowbit(i))
        arr[i] += num;
}
int getsum(int pos){
    int sum = 0;
    for(int i = pos ; i > 0 ; i-= lowbit(i)){
        sum += arr[i];
    }
    return sum;
}

void dfs(int root){
    for(int k = first[root] ; k != -1 ;k = edges[k].next){
        dist[edges[k].v] = dist[root]+edges[k].dis;
        dfs(edges[k].v);
    }
}

void sov(int root ,int step){
   treea[step] = dist[root];
   for(int k = first[root] ; k != -1 ;k = edges[k].next){
        sov(edges[k].v,step+1);
    }
    if(step == 1){
        ans[root] = getsum(1);
        return;
    }
//    for(int i = 1 ; i <= step ; i++)
//        printf("treea[%d] = %I64d,step = %d,au[%d]-dist[%d] = %d- %I64d = %I64d\n",i,treea[i],step,root,root,au[root],dist[root],-au[root]+dist[root]);
    int pos = lowerbound(step,dist[root]-au[root],step);
    if(pos != step){
        add(pos,1);
        add(step,-1);
    }
    ans[root] = getsum(step);
    //printf("ans[%d] = %d\n",root,ans[root]);
    add(step,-ans[root]);
    add(step+1,ans[root]);
}

void print(){
    for(int i = 1 ; i <= n ;i++ )
        printf("%d%c",ans[i],i == n?'\n':' ');
}

int main(){
    scanf("%d",&n);
    init();
    dfs(1);
    sov(1,1);
    print();
}

还有就是区间修改区间查询

http://www.wonter.net/archives/%E6%A0%91%E7%8A%B6%E6%95%B0%E7%BB%84-%E5%8C%BA%E9%97%B4%E6%9B%B4%E6%96%B0-%E5%8C%BA%E9%97%B4%E6%9F%A5%E8%AF%A2.html

sum(x) = a[1] + a[2] + … + a[x] + delta[1] * x + delta[2] * (x - 1) + delta[3] * (x - 2) + … + delta[x] =segma(a[i]) + segma(delta[i] * (x - i + 1)) (1 <= i <= x)=segma(a[i]) + segma(delta[i]) * (x + 1) - segma(delta[i] * i)
delta[i] 和 delta[i] * i 这两个数组会变化,我们可以开两个数组。 c1 表示 delta[i],c2 表示 delta[i] * i 所以上面的更新,根据 c1 和 c2 的定义,因为 delta[a] += d, delta[b + 1] -= d,所以我们只需要将 c1[a] += d, c1[b + 1] -= d,c2[a] += d * a, c2[b + 1] -= d * (b + 1),就可以了。 查询的话,就只需要求前缀和就好了。

你可能感兴趣的:(acm,基本算法)