[点分树][二分] [BZOJ4317]Atm的树 && [BZOJ2117][2010国家集训队]Crash的旅游计划 &&[BZOJ2051]A Problem For Fun

题意


求一棵树上,以每个点为起点的第k短路径


双倍经验三倍经验啊

对于每一个点,二分答案,那么只要判断与当前点距离小于等于二分出来的答案的点的个数是否等于k就行了

复杂度nlog^3n,数据点应该不多……

表示树上倍增常数太大了……

#include 
#include 
#include 
#include 
#include 
#include 
#define N 100010

using namespace std;

int n,k,u,v,w,cnt,root,Max,isize,cnt0,Root;
int G[N],vis[N],iG[N],fa[N],dpt[N],far[N],disr[N];
int p[N][20],Dis[N][20];
struct edge{
    int t,nx,w;
}E[N<<1];
struct stp{
    int a,b,c;
    stp(int _a=0,int _b=0,int _c=0):a(_a),b(_b),c(_c){}
};
struct EDGE{
    int t,nx;
}e[N<<2];
vector<int> dist[N],rec[N];
queue Q;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}

inline void reaD(int &x){
    char c=nc(); x=0;
    for(;c>57||c<48;c=nc());for(;c>=48&&c<=57;x=x*10+c-48,c=nc());
}

inline void Insert(int x,int y,int z){
    E[++cnt].t=y; E[cnt].nx=G[x]; E[cnt].w=z; G[x]=cnt;
    E[++cnt].t=x; E[cnt].nx=G[y]; E[cnt].w=z; G[y]=cnt;
}

inline void Insert(int x,int y){
    e[++cnt].t=y; e[cnt].nx=iG[x]; iG[x]=cnt;
}

int checking(int x,int f){
    int size=1;
    for(int i=G[x];i;i=E[i].nx)
        if(E[i].t!=f&&!vis[E[i].t]) size+=checking(E[i].t,x);
    return size;
}

int explore(int x,int f){
    int Size=1,iMax=0;
    for(int i=G[x];i;i=E[i].nx)
        if(E[i].t!=f&&!vis[E[i].t]){
            int k=explore(E[i].t,x);
            if(k>iMax) iMax=k;
            Size+=k;
        }
    if(isize-Size>iMax) iMax=isize-Size;
    if(iMaxreturn Size;
}

void calc(int x,vector<int> &A,int f,int d){
    A.push_back(d);
    for(int i=G[x];i;i=E[i].nx)
        if(E[i].t!=f&&!vis[E[i].t]) calc(E[i].t,A,x,d+E[i].w);
}

inline int LCA(int x,int y){
    if(dpt[x]return LCA(y,x);
    for(int i=17;~i;i--)
        if(dpt[p[x][i]]>=dpt[y]) x=p[x][i];
    if(x==y) return x;
    for(int i=17;~i;i--)
        if(p[x][i]!=p[y][i]) x=p[x][i],y=p[y][i];
    return p[x][0];
}

inline int dis(int x,int y){
    return disr[x]+disr[y]-2*disr[LCA(x,y)];
}

void divide(int x,int par,int frm){
    isize=checking(x,0);
    Max=1<<30; explore(x,0); int iroot=root;
    if(par) Insert(par,root),fa[root]=par,far[root]=dis(root,par),calc(E[frm].t,rec[root],0,E[frm].w);
    else Root=root;
    vis[root]=1;
    calc(root,dist[root],0,0);
    for(int i=G[iroot];i;i=E[i].nx)
        if(!vis[E[i].t]) divide(E[i].t,iroot,i);
}

inline int find(vector<int> &A,int b){
    return upper_bound(A.begin(),A.end(),b)-A.begin();
}

int query(int x,int d){
    int r=find(dist[x],d)-1,L=0;
    for(int i=x,j=1;fa[i];i=fa[i],j++){
        int k=Dis[x][j];
        if(d-k>=0) r+=find(dist[fa[i]],d-k);
        if(d-k>=0) r-=find(rec[i],d-k);
    }
    return r;
}

void dfs(int x,int f){
    for(int i=1;i<=17;i++) p[x][i]=p[p[x][i-1]][i-1];
    for(int i=G[x];i;i=E[i].nx)
        if(E[i].t!=f){
            p[E[i].t][0]=x;
            dpt[E[i].t]=dpt[x]+1;
            disr[E[i].t]=disr[x]+E[i].w;
            dfs(E[i].t,x);
        }
}

int main(){
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
    reaD(n); reaD(k);
    for(int i=1;i0; dfs(1,0); 
    divide(1,0,0);
    for(int i=1;i<=n;i++){
        int x=fa[i],j=1;
        while(x){
            Dis[i][j]=dis(x,i);
            j++; x=fa[x];
        }
    }
    for(int i=1;i<=n;i++)
        sort(dist[i].begin(),dist[i].end()),sort(rec[i].begin(),rec[i].end());
    for(int i=1;i<=n;i++){
        int L=1,R=1<<30,mid;
        while(L>1)>=k?R=mid:L=mid+1;
        printf("%d\n",R);
    }
}

你可能感兴趣的:(二分,&,三分,点分树)