• BZOJ4012 [HNOI2015]开店 (动态点分治)


    Description

     风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到

    人生哲学。最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱。这样的
    想法当然非常好啦,但是她们也发现她们面临着一个问题,那就是店开在哪里,面
    向什么样的人群。很神奇的是,幻想乡的地图是一个树形结构,幻想乡一共有 n
    个地方,编号为 1 到 n,被 n-1 条带权的边连接起来。每个地方都住着一个妖怪,
    其中第 i 个地方的妖怪年龄是 x_i。妖怪都是些比较喜欢安静的家伙,所以它们并
    不希望和很多妖怪相邻。所以这个树所有顶点的度数都小于或等于 3。妖怪和人一
    样,兴趣点随着年龄的变化自然就会变化,比如我们的 18 岁少女幽香和八云紫就
    比较喜欢可爱的东西。幽香通过研究发现,基本上妖怪的兴趣只跟年龄有关,所以
    幽香打算选择一个地方 u(u为编号),然后在 u开一家面向年龄在 L到R 之间(即
    年龄大于等于 L、小于等于 R)的妖怪的店。也有可能 u这个地方离这些妖怪比较
    远,于是幽香就想要知道所有年龄在 L 到 R 之间的妖怪,到点 u 的距离的和是多
    少(妖怪到 u 的距离是该妖怪所在地方到 u 的路径上的边的权之和) ,幽香把这个
    称为这个开店方案的方便值。幽香她们还没有决定要把店开在哪里,八云紫倒是准
    备了很多方案,于是幽香想要知道,对于每个方案,方便值是多少呢。
     
      

    Input

     第一行三个用空格分开的数 n、Q和A,表示树的大小、开店的方案个数和妖

    怪的年龄上限。 
    第二行n个用空格分开的数 x_1、x_2、…、x_n,x_i 表示第i 个地点妖怪的年
    龄,满足0<=x_i<A。(年龄是可以为 0的,例如刚出生的妖怪的年龄为 0。) 
    接下来 n-1 行,每行三个用空格分开的数 a、b、c,表示树上的顶点 a 和 b 之
    间有一条权为c(1 <= c <= 1000)的边,a和b 是顶点编号。 
    接下来Q行,每行三个用空格分开的数 u、 a、 b。对于这 Q行的每一行,用 a、
    b、A计算出 L和R,表示询问“在地方 u开店,面向妖怪的年龄区间为[L,R]的方
    案的方便值是多少”。对于其中第 1 行,L 和 R 的计算方法为:L=min(a%A,b%A), 
    R=max(a%A,b%A)。对于第 2到第 Q行,假设前一行得到的方便值为 ans,那么当
    前行的 L 和 R 计算方法为: L=min((a+ans)%A,(b+ans)%A), 
    R=max((a+ans)%A,(b+ans)%A)。 
     

    Output

    对于每个方案,输出一行表示方便值。 

     

    Sample Input

    10 10 10 
    0 0 7 2 1 4 7 7 7 9 
    1 2 270 
    2 3 217 
    1 4 326 
    2 5 361 
    4 6 116 
    3 7 38 
    1 8 800 
    6 9 210 
    7 10 278 
    8 9 8 
    2 8 0 
    9 3 1 
    8 0 8 
    4 2 7 
    9 7 3 
    4 7 0 
    2 2 7 
    3 2 1 
    2 3 4

    Sample Output

    1603 
    957 
    7161 
    9466 
    3232 
    5223 
    1879 
    1669 
    1282 
    0

    HINT

     满足 n<=150000,Q<=200000。对于所有数据,满足 A<=10^9


    题解

      这题正解是动态点分治(不过据说主席树+树链剖分跑得更快?)

      如果不知道什么是动态点分的可以去看看幻想乡的战略游戏->蒟蒻的题解

      这一道题,我们考虑对于每一个点分树上的点维护什么。我们记录三个值,$sz_0$表示子树内的点数之和,$sz_1$表示子树内所有点到其的距离之和,$sz_2$表示子树内所有点到其父亲的距离之和。那么考虑我们在跳点分树的时候要如何维护答案呢?很明显$sz_1[u]+sum sz_1[fa]-sz_2[p]+(sz_0[fa]-sz_0[p])*dist(fa,u)$就是在点分树上与$u$的$LCA$是$fa$的所有点的距离之和,其中$fa$为$p$的父亲,$p$为从$u$不断跳父亲直到根,那么只要不断枚举$fa$,并不断往上跳并维护答案就可以了。

      然而上面只是为了方便理解,因为具体的计算不是这样的。我们对于$u$点内部的贡献可以直接计算,然后考虑往上跳。在上述的式子中如果一个点$p$有$fa$,那么答案就要减去$sz_0[p]*dist(fa,u)+sz_2[p]$,如果一个点不是$u$那么答案就要加上$sz_0[p]+dist(p,u)$,然后每一个点都要加上自己的$sz_1$。按这个规律在跳点分树的时候不断加就好了

      然后考虑怎么在$[l,r]$之内,很明显可以搞一个差分,把每一个节点在点分树上的子树内的所有年龄的距离之和加起来,在做前缀和,那么在年龄$[l,r]$的人数就是$[1,r]-[1,l-1]$

      1 //minamoto
      2 #include<cstdio>
      3 #include<iostream>
      4 #include<cstring>
      5 #include<vector>
      6 #include<algorithm>
      7 #define ll long long
      8 #define N 150005
      9 using namespace std;
     10 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
     11 char buf[1<<21],*p1=buf,*p2=buf;
     12 template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
     13 inline int read(){
     14     #define num ch-'0'
     15     char ch;bool flag=0;int res;
     16     while(!isdigit(ch=getc()))
     17     (ch=='-')&&(flag=true);
     18     for(res=num;isdigit(ch=getc());res=res*10+num);
     19     (flag)&&(res=-res);
     20     #undef num
     21     return res;
     22 }
     23 char sr[1<<21],z[20];int C=-1,Z;
     24 inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
     25 inline void print(ll x){
     26     if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x;
     27     while(z[++Z]=x%10+48,x/=10);
     28     while(sr[++C]=z[Z],--Z);sr[++C]='
    ';
     29 }
     30 int head[N],Next[N<<1],ver[N<<1],edge[N<<1];
     31 int n,tot,val[N],q,maxn;
     32 int st[N<<1][19],d[N],dfn[N],num,bin[25],tp,logn[N<<1];
     33 inline void add(int u,int v,int e){
     34     ver[++tot]=v,Next[tot]=head[u],head[u]=tot,edge[tot]=e;
     35     ver[++tot]=u,Next[tot]=head[v],head[v]=tot,edge[tot]=e;
     36 }
     37 inline void ST(){
     38     for(int j=1;j<=tp;++j)
     39     for(int i=1;i+bin[j]-1<=(n<<1);++i)
     40     st[i][j]=min(st[i][j-1],st[i+bin[j-1]][j-1]);
     41 }
     42 void dfs1(int u,int fa){
     43     st[dfn[u]=++num][0]=d[u];
     44     for(int i=head[u];i;i=Next[i]){
     45         int v=ver[i];
     46         if(v==fa) continue;
     47         d[v]=d[u]+edge[i],dfs1(v,u),st[++num][0]=d[u];
     48     }
     49 }
     50 int fa[N],sz[N],son[N],size,rt;bool vis[N];
     51 void dfs2(int u,int fa){
     52     sz[u]=1,son[u]=0;
     53     for(int i=head[u];i;i=Next[i]){
     54         int v=ver[i];
     55         if(vis[v]||v==fa) continue;
     56         dfs2(v,u),sz[u]+=sz[v],cmax(son[u],sz[v]);
     57     }
     58     cmax(son[u],size-sz[u]);
     59     if(son[u]<son[rt]) rt=u;
     60 }
     61 inline ll dis(int a,int b){
     62     if(dfn[a]>dfn[b]) a^=b^=a^=b;
     63     int k=logn[dfn[b]-dfn[a]+1];
     64     return d[a]+d[b]-(min(st[dfn[a]][k],st[dfn[b]-bin[k]+1][k])<<1);
     65 }
     66 struct node{
     67     int val;ll sz[3];
     68     node(int a=0,ll b=0,ll c=0,ll d=0){val=a,sz[0]=b,sz[1]=c,sz[2]=d;}
     69     inline bool operator <(const node &b)const
     70     {return val<b.val;}
     71 };
     72 vector<node> sta[N];
     73 void dfs3(int u,int f,int rt){
     74     sta[rt].push_back(node(val[u],1,dis(u,rt),fa[rt]?dis(u,fa[rt]):0));
     75     for(int i=head[u];i;i=Next[i]){
     76         int v=ver[i];
     77         if(v==f||vis[v]) continue;
     78         dfs3(v,u,rt);
     79     }
     80 }
     81 void dfs4(int u){
     82     vis[u]=true;
     83     dfs3(u,0,u);sta[u].push_back(node(-1,0,0,0));
     84     sort(sta[u].begin(),sta[u].end());
     85     for(int i=0,j=sta[u].size();i<j-1;++i)
     86     sta[u][i+1].sz[0]+=sta[u][i].sz[0],
     87     sta[u][i+1].sz[1]+=sta[u][i].sz[1],
     88     sta[u][i+1].sz[2]+=sta[u][i].sz[2];
     89     for(int i=head[u];i;i=Next[i]){
     90         int v=ver[i];
     91         if(vis[v]) continue;
     92         rt=0,size=sz[v];
     93         dfs2(v,0),fa[rt]=u,dfs4(rt);
     94     }
     95 }
     96 inline node query(int id,int l,int r){
     97     if(id==0) return node();
     98     vector<node>::iterator it1=upper_bound(sta[id].begin(),sta[id].end(),node(r,0,0,0));--it1;
     99     vector<node>::iterator it2=upper_bound(sta[id].begin(),sta[id].end(),node(l-1,0,0,0));--it2;
    100     return node(0,it1->sz[0]-it2->sz[0],it1->sz[1]-it2->sz[1],it1->sz[2]-it2->sz[2]);
    101 }
    102 inline ll calc(int u,int l,int r){
    103     ll res=0;
    104     for(int p=u;p;p=fa[p]){
    105         node a=query(p,l,r);
    106         res+=a.sz[1];
    107         if(p!=u) res+=a.sz[0]*dis(p,u);
    108         if(fa[p]) res-=a.sz[2]+a.sz[0]*dis(fa[p],u);
    109     }
    110     return res;
    111 }
    112 int main(){
    113     ll ans=0;
    114     n=read(),q=read(),maxn=read();
    115     bin[0]=1,logn[0]=-1;
    116     for(int i=1;i<=20;++i) bin[i]=bin[i-1]<<1;
    117     while(bin[tp+1]<=(n<<1)) ++tp;
    118     for(int i=1;i<=(n<<1);++i) logn[i]=logn[i>>1]+1;
    119     for(int i=1;i<=n;++i) val[i]=read();
    120     for(int i=1;i<n;++i){
    121         int u=read(),v=read(),e=read();
    122         add(u,v,e);
    123     }
    124     dfs1(1,0),ST();
    125     rt=0,son[0]=n+1,size=n,dfs2(1,0);
    126     dfs4(rt);
    127     while(q--){
    128         int a=read(),b=read(),c=read();
    129         b=(b+ans)%maxn,c=(c+ans)%maxn;
    130         if(b>c) b^=c^=b^=c;
    131         print(ans=calc(a,b,c));
    132     }
    133     Ot();
    134     return 0;
    135 }
  • 相关阅读:
    poj 3070 矩阵快速乘
    将数字放大显示
    hdu 火车入队 stack
    linq to Entity 数据库除了有主键还有唯一索引,是不是不能更新
    整理:C#写ActiveX, 从代码到打包到签名到发布的示例
    Java—FileOperator
    第二章-递归与分治策略
    第四章-贪心算法
    第五章-回溯法
    Linux中C程序调试、makefile
  • 原文地址:https://www.cnblogs.com/bztMinamoto/p/9480501.html
Copyright © 2020-2023  润新知