时间限制:
20000ms
单点时限:
1000ms
内存限制:
256MB
-
3 1 1 2 2 3 1 3
样例输出
-
1
描述
幻想乡一共有n处居所,编号从1到n。这些居所被n-1条边连起来,形成了一个树形的结构。
每处居所都居住着一个小精灵。每天小精灵们都会选出一个区间[l,r],居所编号在这个区间内的小精灵一起来完成一项任务。
特别的,居所相邻的(有边相连的)两个小精灵会自发的组成一队,并且如果a和b相邻b和c相邻,那么a和c也在同一队里面。每天的任务完成之后,队伍就会解散;第二天再根据新的区间组成新的队伍。
给出每天小精灵们选出的区间,你知道每天组成的队伍数量吗?
输入
第一行两个数n和Q(1 <= n, Q <= 100000),表示居所的数目和组队的天数。
接下来n-1行,每行两个数a和b,表示居所a和b之间有一条边。
接下来Q行,每行两个数l和r,满足1<=l<=r<=n,为该天小精灵选出的区间。
输出
输出Q行,每行一个整数表示该天队伍的数量。
题解:由于图是树,所以这题有个很重要的性质:对于一个点区间 [L,R] 的联通块的数目,等于这个区间的点数,减去这个区间中的点与点之间的边数。
对于边(u,v),u<v,看成一个子区间 [u,v]。那么问题就转换为求 [L,R] 区间内包含多少个子区间 。 这是一个经典的问题。离线处理询问,按R值从小到大排序。从1扫到n,碰到一个子区间的右值v则在左值u处加1。当我们碰到询问的右值r的时候,这是所有访问过的子区间的右值都<=R,由于我们在已访问过的子区间的左值处加1,这时区间[L,R]的和就是在该区间中的子区间的数目。
代码如下:
#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<string.h>
#define mod 1000000007
#define nn 110000
typedef long long LL;
using namespace std;
struct node
{
int en,next;
}E[nn*2];
int n,q;
int p[nn],num;
void init()
{
memset(p,-1,sizeof(p));
num=0;
}
void add(int st,int en)
{
E[num].en=en;
E[num].next=p[st];
p[st]=num++;
}
struct ask
{
int id;
int l,r;
}a[nn];
int ans[nn];
bool cmp(ask x,ask y)
{
return x.r<y.r;
}
int tree[nn];
inline int lowbit(int x)
{
return (x&-x);
}
void jia(int id,int x)
{
for(int i=id;i<=n;i+=lowbit(i))
{
tree[i]+=x;
}
}
int sum(int id)
{
int re=0;
while(id)
{
re+=tree[id];
id-=lowbit(id);
}
return re;
}
int main()
{
int i,u,v;
while(scanf("%d%d",&n,&q)!=EOF)
{
init();
for(i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
for(i=1;i<=q;i++)
{
scanf("%d%d",&a[i].l,&a[i].r);
a[i].id=i;
}
sort(a+1,a+q+1,cmp);
memset(tree,0,sizeof(tree));
int ix=1,w;
for(i=1;i<=n;i++)
{
for(int j=p[i];j+1;j=E[j].next)
{
w=E[j].en;
if(w<i)
{
jia(w,1);
}
}
while(i==a[ix].r&&ix<=q)
{
ans[a[ix].id]=a[ix].r-a[ix].l+1-(sum(a[ix].r)-sum(a[ix].l-1));
ix++;
}
}
for(i=1;i<=q;i++)
{
printf("%d\n",ans[i]);
}
}
return 0;
}