1. 程式人生 > >BZOJ4317Atm的樹&BZOJ2051A Problem For Fun&BZOJ2117[2010國家集訓隊]Crash的旅遊計劃——二分答案+動態點分治(點分樹套線段樹/點分樹+vector)

BZOJ4317Atm的樹&BZOJ2051A Problem For Fun&BZOJ2117[2010國家集訓隊]Crash的旅遊計劃——二分答案+動態點分治(點分樹套線段樹/點分樹+vector)

題目描述

Atm有一段時間在虐qtree的題目,於是,他滿腦子都是tree,tree,tree…… 於是,一天晚上他夢到自己被關在了一個有根樹中,每條路徑都有邊權,一個神祕的聲音告訴他,每個點到其他的點有一個距離(什麼是距離不用說吧),他需要對於每個點回答:從這個點出發的第k小距離是多少; 如果atm不能回答出來,那麼明天4019的鬧鐘將不會響,4019全寢可能就遲到了,所以atm希望你幫幫他。

輸入

第一行,兩個正整數n,k,表示樹的點數,詢問的是第幾小距離; 第二~n行,每行三個正整數x,y,w,表示x和y之間有一條邊,x為父親,邊權為w;

輸出

n行, 每行一個數,第i行輸出從i開始第k小距離

樣例輸入

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

樣例輸出

4
5
10
9
6

提示

100% n<=15000, 邊權在1~10之間,為了方便,保證1為根;K<=5000   我們先考慮對於單次詢問如何用點分治來解決。 對於每個分治中心,如果它處理的聯通塊中包含查詢點,那麼需要記錄下來聯通塊中所有點到分治中心的距離+分治中心到查詢點的距離,然後容斥去掉與查詢點位於分治中心同一棵子樹中的點,遞迴下一層,最後在所有記錄的距離中取第k小的。
那麼轉化到點分樹上每個點要記錄子樹中所有點到這個點的距離及子樹中所有點到這個點在點分樹上父節點的距離,這個資訊在每個點開兩棵線段樹分別記錄一下即可。 因為需要查詢多個點的資訊併合並,所以不能直接求出第k小,要二分答案然後驗證。 BZOJ2117線段樹卡記憶體、平衡樹卡時間,但你發現所有查詢都在資訊新增之後,所以每個點開兩個vector然後排序,每次查詢在vector上二分查詢即可。 時間複雜度O(nlogn^3)。 點分樹套線段樹
#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
int root[15010];
int froot[15010];
int ls[6000010];
int rs[6000010];
int sum[6000010];
int n,k;
int x,y,z;
int tot;
int num;
int dfn;
int f[15010];
int g[30010][16];
int lg[30010];
int dep[15010];
int val[30010];
int to[30010];
int next[30010];
int head[15010];
int size[15010];
int s[15010];
int rot;
int cnt;
int mx[15010];
int ans;
int vis[15010];
int l,r;
inline void add(int x,int y,int z)
{
    tot++;
    next[tot]=head[x];
    head[x]=tot;
    to[tot]=y;
    val[tot]=z;
}
inline void dfs(int x,int fa)
{
    g[++dfn][0]=dep[x];
    s[x]=dfn;
    for(int i=head[x];i;i=next[i])
    {
        if(to[i]!=fa)
        {
            dep[to[i]]=dep[x]+val[i];
            dfs(to[i],x);
            g[++dfn][0]=dep[x];
        }
    }
}
inline void getroot(int x,int fa)
{
    size[x]=1;
    mx[x]=0;
    for(int i=head[x];i;i=next[i])
    {
        if(!vis[to[i]]&&to[i]!=fa)
        {
            getroot(to[i],x);
            size[x]+=size[to[i]];
            mx[x]=max(mx[x],size[to[i]]);
        }
    }
    mx[x]=max(mx[x],num-size[x]);
    if(mx[x]<mx[rot])
    {
        rot=x;
    }
}
inline int lca(int x,int y)
{
    x=s[x];
    y=s[y];
    if(x>y)
    {
        swap(x,y);
    }
    int len=lg[y-x+1];
    return min(g[x][len],g[y-(1<<len)+1][len]);
}
inline int dis(int x,int y)
{
    return dep[x]+dep[y]-2*lca(x,y);
}
inline void partation(int x)
{
    vis[x]=1;
    for(int i=head[x];i;i=next[i])
    {
        if(!vis[to[i]])
        {
            num=size[to[i]];
            rot=0;
            getroot(to[i],0);
            f[rot]=x;
            partation(rot);
        }
    }
}
inline void change(int &rt,int l,int r,int k)
{
    if(!rt)
    {
        rt=++cnt;
    }
    sum[rt]++;
    if(l==r)
    {
        return ;
    }
    int mid=(l+r)>>1;
    if(k<=mid)
    {
        change(ls[rt],l,mid,k);
    }
    else
    {
        change(rs[rt],mid+1,r,k);
    }
}
inline int query(int rt,int l,int r,int k)
{
    if(!rt||k<0)
    {
        return 0;
    }
    if(l==r)
    {
        return sum[rt];
    }
    int mid=(l+r)>>1;
    if(k<=mid)
    {
        return query(ls[rt],l,mid,k);
    }
    else
    {
        return sum[ls[rt]]+query(rs[rt],mid+1,r,k);
    }
}
inline int check(int val,int x)
{
    int res=0;
    for(int i=x;i;i=f[i])
    {
        res+=query(root[i],0,150000,val-dis(x,i));
    }
    for(int i=x;f[i];i=f[i])
    {
        res-=query(froot[i],0,150000,val-dis(x,f[i]));
    }
    return res;
}
int main()
{
    scanf("%d%d",&n,&k);
    k++;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
        add(y,x,z);
    }
    dfs(1,0);
    for(int i=2;i<=dfn;i++)
    {
        lg[i]=lg[i>>1]+1;
    }
    for(int j=1;j<=15;j++)
    {
        for(int i=1;i<=dfn;i++)
        {
            if(i+(1<<j)-1>dfn)
            {
                break;
            }
            g[i][j]=min(g[i][j-1],g[i+(1<<(j-1))][j-1]);
        }
    }
    mx[0]=1<<30;
    num=n;
    rot=0;
    getroot(1,0);
    partation(rot);
    for(int x=1;x<=n;x++)
    {
        for(int i=x;i;i=f[i])
        {
            change(root[i],0,150000,dis(x,i));
        }
        for(int i=x;f[i];i=f[i])
        {
            change(froot[i],0,150000,dis(x,f[i]));
        }
    }
    for(int i=1;i<=n;i++)
    {
        l=0;
        r=150000;
        ans=-1;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(check(mid,i)>=k)
            {
                ans=mid;
                r=mid-1;
            }
            else
            {
                l=mid+1;
            }
        }
        printf("%d\n",ans);
    }
}

點分樹+vector

#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
vector<int>sum[100010];
vector<int>fsum[100010];
int n,k;
int x,y,z;
int tot;
int num;
int dfn;
char ch[3];
int f[100010];
int g[200010][17];
int lg[200010];
int dep[100010];
int val[200010];
int to[200010];
int next[200010];
int head[100010];
int size[100010];
int s[100010];
int rot;
int cnt;
int mx[100010];
int ans;
int vis[100010];
int l,r;
int length;
void add(int x,int y,int z)
{
    tot++;
    next[tot]=head[x];
    head[x]=tot;
    to[tot]=y;
    val[tot]=z;
}
void dfs(int x,int fa)
{
    g[++dfn][0]=dep[x];
    s[x]=dfn;
    for(int i=head[x];i;i=next[i])
    {
        if(to[i]!=fa)
        {
            dep[to[i]]=dep[x]+val[i];
            dfs(to[i],x);
            g[++dfn][0]=dep[x];
        }
    }
}
void getroot(int x,int fa)
{
    size[x]=1;
    mx[x]=0;
    for(int i=head[x];i;i=next[i])
    {
        if(!vis[to[i]]&&to[i]!=fa)
        {
            getroot(to[i],x);
            size[x]+=size[to[i]];
            mx[x]=max(mx[x],size[to[i]]);
        }
    }
    mx[x]=max(mx[x],num-size[x]);
    if(mx[x]<mx[rot])
    {
        rot=x;
    }
}
int lca(int x,int y)
{
    x=s[x];
    y=s[y];
    if(x>y)
    {
        swap(x,y);
    }
    int len=lg[y-x+1];
    return min(g[x][len],g[y-(1<<len)+1][len]);
}
int dis(int x,int y)
{
    return dep[x]+dep[y]-(lca(x,y)<<1);
}
void partation(int x)
{
    vis[x]=1;
    for(int i=head[x];i;i=next[i])
    {
        if(!vis[to[i]])
        {
            num=size[to[i]];
            rot=0;
            getroot(to[i],0);
            f[rot]=x;
            partation(rot);
        }
    }
}
int check(int val,int x)
{
    int res=0;
    for(int i=x;i;i=f[i])
    {
        res+=upper_bound(sum[i].begin(),sum[i].end(),val-dis(x,i))-sum[i].begin();
    }
    for(int i=x;f[i];i=f[i])
    {
        res-=upper_bound(fsum[i].begin(),fsum[i].end(),val-dis(x,f[i]))-fsum[i].begin();
    }
    return res;
}
int main()
{
    scanf("%s",ch);
    scanf("%d%d",&n,&k);
    k++;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        length+=z;
        add(x,y,z);
        add(y,x,z);
    }
    dfs(1,0);
    for(int i=2;i<=dfn;i++)
    {
        lg[i]=lg[i>>1]+1;
    }
    for(int j=1;j<=16;j++)
    {
        for(int i=1;i<=dfn;i++)
        {
            if(i+(1<<j)-1>dfn)
            {
                break;
            }
            g[i][j]=min(g[i][j-1],g[i+(1<<(j-1))][j-1]);
        }
    }
    mx[0]=1<<30;
    num=n;
    rot=0;
    getroot(1,0);
    partation(rot);
    for(int x=1;x<=n;x++)
    {
        for(int i=x;i;i=f[i])
        {
            sum[i].push_back(dis(i,x));
        }
        for(int i=x;f[i];i=f[i])
        {
            fsum[i].push_back(dis(f[i],x));
        }
    }
    for(int i=1;i<=n;i++)
    {
        sort(sum[i].begin(),sum[i].end());
        sort(fsum[i].begin(),fsum[i].end());
    }
    for(int i=1;i<=n;i++)
    {
        l=0;
        r=length;
        ans=-1;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(check(mid,i)>=k)
            {
                ans=mid;
                r=mid-1;
            }
            else
            {
                l=mid+1;
            }
        }
        printf("%d\n",ans);
    }
}