BZOJ 4012 HNOI2015 开店 树的边分治+分治树

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4012

 

题意概述:给出一颗N点的树,保证树上所有点的度不超过3,树上每个点有权值,每条边有权值,现在有Q组询问,每组给出信息u,L,R,问点权在区间[L,R]的点到点u的距离和为多少。强制在线。

N<=150000,Q<=200000.

 

可能这是我这几天做过的题里面最水但是最码的一个了。。。。

实际上看见树上所有点的度不超过3就感觉可以用边分治做,具体的思路实际上很简单,建立一个边分治结构,这个结构大约有logN层,每层有N个点,对于每组询问,看询问的点u在当前树被分成的两棵子树中的哪一棵,统计出另一个子树中所有点权在询问区间内的点到u的距离,然后递归到u所在的子树,最终就可以计算出答案。

重点在于实现,一不小心处理的时候就会多写出几个常数出来。。。(不要问我是怎么知道的)

实际上这个题我yy的东西重点就在于把边分治建立成一个二叉树的结构然后在里面去搞事情(小火车万岁!),最多有2N个分治结构(每条边会产生2个,有N-1条边)。每一层每个点只会有一个信息,利用这个性质可以记录很多东西,询问的时候在分治结构中递归,令当前分治结构中询问点所在的子树根节点为x,另一边的子树根节点为y(实际上就是断掉的边的两个端点),每一次询问的时候就在y的信息里面二分计算询问区间内点到y的距离,同时得到这些点的数量,然后把从y经过x到u的距离补全累加到答案中,递归。(要点已经交代完了,剩下的请各位自己yy,手动滑稽)

预处理可以用归并排序做到O(nlogn),主要复杂度在查询,单次是O(log2n)。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<cctype>
using namespace std;
const int maxn=150005;
const int maxd=45;
typedef long long LL;

int N,Q,A,L,R,age[maxn]; LL ans,dist[maxd][maxn],sum[maxd][maxn];
struct edge{ int to,next,w; }E[maxn<<1];
int first[maxn],np,sz[maxn],rt[maxd][maxn];
int ID[maxd][maxn],cnt;
int ke[maxn<<1],MAX,ch[maxn<<1][2],l[maxn<<1],r[maxn<<1],size[maxd],tot[maxn<<1];
bool vis[maxn<<1];

void _scanf(int &x)
{
    x=0;
    char cha=getchar();
    while(cha<'0'||cha>'9') cha=getchar();
    while(cha>='0'&&cha<='9') x=x*10+cha-'0',cha=getchar();
}
int out_cnt,out[22];
void _printf(LL x)
{
    out_cnt=0;
    out[++out_cnt]=x%10,x/=10;
    while(x) out[++out_cnt]=x%10,x/=10;
    while(out_cnt) putchar('0'+out[out_cnt--]);
}
void add_edge(int u,int v,int w)
{
    E[++np]=(edge){v,first[u],w};
    first[u]=np;
}
void data_in()
{
    _scanf(N);_scanf(Q);_scanf(A);
    for(int i=1;i<=N;i++) _scanf(age[i]);
    int x,y,z;
    for(int i=1;i<N;i++){
        _scanf(x);_scanf(y);_scanf(z);
        add_edge(x,y,z);
        add_edge(y,x,z);
    }
}
void DFS(int i,int f,int SZ,int id,int d,LL l)
{
    sz[i]=1,size[d]++,dist[d][i]=l;
    for(int p=first[i];p;p=E[p].next){
        int j=E[p].to;
        if(j==f||vis[p]) continue;
        rt[d][j]=rt[d][i];
        DFS(j,i,SZ,id,d,l+E[p].w);
        sz[i]+=sz[j];
        int tmp=max(sz[j],SZ-sz[j]);
        if(tmp<MAX) MAX=tmp,ke[id]=p;
    }
}
void merge_sort(int x,int d)
{
    int pos=l[x],i=l[ch[x][0]],j=r[ch[x][0]],r1=r[ch[x][0]],r2=r[ch[x][1]];
    while(i<r1&&j<r2) ID[d][pos++]=age[ID[d+1][i]]<age[ID[d+1][j]]?ID[d+1][i++]:ID[d+1][j++];
    while(i<r1) ID[d][pos++]=ID[d+1][i++];
    while(j<r2) ID[d][pos++]=ID[d+1][j++];
}
void div_tree(int i,int SZ,int id,int d)
{
    MAX=SZ;
    l[id]=size[d],rt[d][i]=i;
    DFS(i,0,SZ,id,d,0);
    r[id]=size[d];
    if((tot[id]=SZ)==1){ ID[d][l[id]]=i; return; }
    int o0=E[ke[id]].to,o1=E[(ke[id]-1^1)+1].to;
    int _sz0=sz[o0],_sz1=SZ-sz[o0];
    vis[ke[id]]=vis[(ke[id]-1^1)+1]=1;
    div_tree(o0,_sz0,ch[id][0]=++cnt,d+1);
    div_tree(o1,_sz1,ch[id][1]=++cnt,d+1);
    merge_sort(id,d);
    sum[d][l[id]]=dist[d][ID[d][l[id]]];
    for(int j=l[id]+1;j<r[id];j++) sum[d][j]=sum[d][j-1]+dist[d][ID[d][j]];
}
bool cmp(int x,int y){ return age[x]<age[y]; }
void solve(int p,int id,int d)
{
    if(tot[id]==1) return;
    int x=E[ke[id]].to,y=E[(ke[id]-1^1)+1].to;
    int dd=rt[d][p]!=x,t=ch[id][dd^1];
    age[0]=L;
    int ll=lower_bound(ID[d]+l[t],ID[d]+r[t],0,cmp)-ID[d]-1;
    LL v1=ll>=l[t]?sum[d][ll]:0;
    age[0]=R;
    int rr=upper_bound(ID[d]+l[t],ID[d]+r[t],0,cmp)-ID[d]-1;
    LL v2=rr>=l[t]?sum[d][rr]:0;
    ans+=v2-v1+(rr-ll)*(E[ke[id]].w+dist[d][p]);
    solve(p,ch[id][dd],d+1);
}
void work()
{
    div_tree(1,N,++cnt,1);
    int u,a,b;
    for(int i=1;i<=Q;i++){
        _scanf(u);_scanf(a);_scanf(b);
        L=min((a+ans)%A,(b+ans)%A);
        R=max((a+ans)%A,(b+ans)%A);
        ans=0;
        solve(u,1,2);
        _printf(ans),putchar('\n');
    }
}
int main()
{
    data_in();
    work();
    return 0;
}

 

posted @ 2018-02-28 10:46  KKKorange  阅读(529)  评论(0编辑  收藏  举报