FZOJ 2245 动态树(离散+离线+ 树状数组)

Posted 贱人方

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FZOJ 2245 动态树(离散+离线+ 树状数组)相关的知识,希望对你有一定的参考价值。

Problem 2245 动态树

Accept: 17    Submit: 82
Time Limit: 3000 mSec    Memory Limit : 65536 KB

技术分享 Problem Description

YellowStar拥有一棵神奇的动态树,该树由n个带权结点,n-1条边构成,任意两个结点互相可达,标号为i结点的权值为Wi。

由于物质是运动的,这棵树每天都会发生一些变化,在第i天,该树权值在[l,r]的结点会发出强烈的光芒,YellowStar看到这一现象会非常的愉悦,它的愉悦值取决于:发光的这些结点构成了多少个连通子树。

现在你知道动态树在接下来m天的发光情况,请你计算出YellowStar每一天的愉悦值

技术分享 Input

第一行输入T,表示有T组样例(T <= 20)

 

每组样例第一行为n,表示树的结点个数(1 <= n <= 1e5)

 

接下来n个数字W1, W2, ... , Wn (1 <= Wi <= 1e9)表示每个结点的权值

 

接下来n - 1行,每一行两个数字u, v (1 <= u, v <= n)表示树边

 

接下来一个数字m(1 <= m <= 1e5),表示m天

 

接下来m个询问,每个询问一行l, r (1 <= l <= r <= 1e9)表示每一天发光的结点的权值区间

技术分享 Output

每组样例输出m行,表示YellowStar在这m天的愉悦值

技术分享 Sample Input

1 3 1 3 2 1 2 1 3 3 1 3 1 2 2 3

技术分享 Sample Output

1 1 2

技术分享 Hint

long long类型请用%I64d输出

技术分享 Source

FOJ有奖月赛-2017年4月(校赛热身赛)
【分析】给你一棵树,每个节点有一个权值([1,1e9]),然后Q次询问,每次给出一个区间[l,r],问权值处在这个区间内的节点构成的联通块有多少个。
 一开始想到的是并查集+莫队,后来发现并查集那边不好处理。听了学长的,先将所有权值离散,区间离线,按照右端点从小到大排序。然后对于,每一条边,也看成一个区间
跟前面一样排序。然后,对于一次查询区间,这个区间里有多少点这个我们是可以预处理出来的,假设是S个,也就是没有边的时候联通块是S个,若其中有两个点连有一条边,
那么联通块-1,有多少边,S就减多少。现在的问题转化成了查询区间内有多少边。由于我们是排了序的,所以我们固定查询的右端点,对于右端点<=查询右端点的边,用树状数组
统计<=右端点的边有多少就行了。
#include <cstdio>
#include <vector>
#include <cstring>
#include <string>
#include <cstdlib>
#include <iostream>
#include <map>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
typedef pair<int,int>pii;
const int N = 1e5+5;
const double eps = 1e-8;
int T,n,w[N],sum[N<<2],p[N<<2],cnt,m,ret[N],c[N<<2];
struct Query{
  int l,r,id;
  bool operator<(const Query &rhs)const{
      return r<rhs.r;
  }
}q[N],t[N];
void add(int x){
   for(;x<=cnt;x+=x&-x)++c[x];
}
int ask(int x){
   int ret = 0;
   for(;x>0;x-=x&-x)ret+=c[x];
   return ret;
}
int main() {
   scanf("%d",&T);
   while(T--){
     scanf("%d",&n);
     cnt = 0;
     for(int i=1;i<=n;++i){
       scanf("%d",&w[i]);
       p[++cnt]=w[i];
     } 
     for(int i=1;i<n;++i){
        scanf("%d%d",&t[i].l,&t[i].r);
        t[i].l=w[t[i].l];
        t[i].r=w[t[i].r];
     }
     scanf("%d",&m);
     for(int i=1;i<=m;++i){
        scanf("%d%d",&q[i].l,&q[i].r);
        p[++cnt]=q[i].l;
        p[++cnt]=q[i].r;
        q[i].id=i;
     }
     sort(p+1,p+1+cnt);
     cnt=unique(p+1,p+1+cnt)-p-1;
     for(int i=0;i<=cnt;++i)c[i]=sum[i]=0;
     for(int i=1;i<=n;++i){
        w[i]=lower_bound(p+1,p+1+cnt,w[i])-p;
        ++sum[w[i]];
     }
     for(int i=1;i<=cnt;++i)sum[i]+=sum[i-1];
     for(int i=1;i<n;++i){
        t[i].l=lower_bound(p+1,p+1+cnt,t[i].l)-p;
        t[i].r=lower_bound(p+1,p+1+cnt,t[i].r)-p;
        if(t[i].l>t[i].r)swap(t[i].l,t[i].r);
     }
     for(int i=1;i<=m;++i){
        q[i].l=lower_bound(p+1,p+1+cnt,q[i].l)-p;
        q[i].r=lower_bound(p+1,p+1+cnt,q[i].r)-p;
     }
     if(n!=1)sort(t+1,t+1+n-1);
     sort(q+1,q+1+m);
     int j=1;
     for(int i=1;i<=m;++i){
       ret[q[i].id]=sum[q[i].r]-sum[q[i].l-1];
       for(;j<=n-1&&t[j].r<=q[i].r;++j)add(t[j].l);
       ret[q[i].id]-=ask(q[i].r)-ask(q[i].l-1);
     }
     for(int i=1;i<=m;++i)printf("%d\n",ret[i]); 
   }
   return 0;
}

 

 

以上是关于FZOJ 2245 动态树(离散+离线+ 树状数组)的主要内容,如果未能解决你的问题,请参考以下文章

HDU 5249 离线树状数组求第k大+离散化

[hdu 4417]树状数组+离散化+离线处理

利用id来进行树状数组,而不是离散化以后的val HDU 4417 离线+树状数组

求区间不同数的个数

BZOJ 1935: [Shoi2007]Tree 园丁的烦恼 [树状数组 离线 离散化]

ZZUOJ 10508 树状数组