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; }