1. 程式人生 > >BZOJ_3653_談笑風生_樹狀數組

BZOJ_3653_談笑風生_樹狀數組

AS 之間 接下來 pan bool while fix clas 除了

BZOJ_3653_談笑風生_樹狀數組

Description

設T 為一棵有根樹,我們做如下的定義: ? 設a和b為T 中的兩個不同節點。如果a是b的祖先,那麽稱“a比b不知道 高明到哪裏去了”。 ? 設a 和 b 為 T 中的兩個不同節點。如果 a 與 b 在樹上的距離不超過某個給定 常數x,那麽稱“a 與b 談笑風生”。 給定一棵n個節點的有根樹T,節點的編號為1 到 n,根節點為1號節點。你需 要回答q 個詢問,詢問給定兩個整數p和k,問有多少個有序三元組(a;b;c)滿足: 1. a、b和 c為 T 中三個不同的點,且 a為p 號節點; 2. a和b 都比 c不知道高明到哪裏去了; 3. a和b 談笑風生。這裏談笑風生中的常數為給定的 k。

Input

第一行含有兩個正整數n和q,分別代表有根樹的點數與詢問的個數。 接下來n - 1行,每行描述一條樹上的邊。每行含有兩個整數u和v,代表在節點u和v之間有一條邊。 接下來q行,每行描述一個操作。第i行含有兩個整數,分別表示第i個詢問的p和k。 1<=P<=N 1<=K<=N N<=300000 Q<=300000

Output

輸出 q 行,每行對應一個詢問,代表詢問的答案。

Sample Input

5 3
1 2
1 3
2 4
4 5
2 2
4 1
2 3

Sample Output

3
1
3

b有位置兩種可能,a的祖先和a的子樹。 如果b是a的祖先,則c只能選擇a子樹中除了a的一個。 如果b是a的子樹中的一個點,c只能選擇b子樹除了b的一個。 於是問題轉化為子樹裏距離小於等於k的子樹大小和。 兩個限制條件:子樹內和深度小於等於一個值。 把每個點的dfs序位置和深度看成兩個坐標,轉化為二維數點,同樹狀數組解決。 代碼:
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define N 300050
typedef long long ll;
int head[N],to[N<<1],nxt[N<<1],cnt,n,m;
int dep[N],siz[N],dfn[N],S[N],son[N];
ll c[N],ans[N];
inline void add(int u,int v) {
    to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt;
}
void fix(int x,int v) {
    for(;x<=n;x+=x&(-x)) c[x]+=v;
}
ll inq(int x) {
    ll re=0; for(;x;x-=x&(-x)) re+=c[x]; return re;
}
struct QAQ {
    int p,d,id,opt;
}a[N<<1];
bool cmp(const QAQ &x,const QAQ &y) {
    if(x.p==y.p) return x.opt<y.opt; 
    return x.p<y.p;
}
void dfs(int x,int y) {
    int i; S[++S[0]]=x; dfn[x]=S[0]; dep[x]=dep[y]+1; siz[x]=1;
    for(i=head[x];i;i=nxt[i]) if(to[i]!=y) {
        dfs(to[i],x); siz[x]+=siz[to[i]];
    }
    son[x]=S[0];
}
int main() {
    scanf("%d%d",&n,&m);
    int i,x,y;
    for(i=1;i<n;i++) {
        scanf("%d%d",&x,&y); add(x,y); add(y,x);
    }
    dfs(1,0);
    int tot=0;
    for(i=1;i<=m;i++) {
        scanf("%d%d",&x,&y); ans[i]=1ll*min(y,dep[x]-1)*(siz[x]-1);
        int depp=min(dep[x]+y,n);
        a[++tot].p=dfn[x]; a[tot].opt=-1; a[tot].d=depp; a[tot].id=i;
        a[++tot].p=son[x]; a[tot].opt=1;  a[tot].d=depp; a[tot].id=i;
    }
    sort(a+1,a+tot+1,cmp);
    int now=0;
    for(i=1;i<=tot;i++) {
        while(now<=n&&now<a[i].p) now++,fix(dep[S[now]],siz[S[now]]-1);
        ans[a[i].id]+=a[i].opt*inq(a[i].d);
    }
    for(i=1;i<=m;i++) printf("%lld\n",ans[i]);
}

BZOJ_3653_談笑風生_樹狀數組