洛谷 P3241 [HNOI2015]开店 解题报告

P3241 [HNOI2015]开店

题目描述

风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到人生哲学。最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱。

这样的想法当然非常好啦,但是她们也发现她们面临着一个问题,那就是店开在哪里,面向什么样的人群。很神奇的是,幻想乡的地图是一个树形结构,幻想乡一共有 \(n\)个地方,编号为 \(1\)\(n\)\(n-1\) 条带权的边连接起来。每个地方都住着一个妖怪,其中第 \(i\) 个地方的妖怪年龄是 \(x_i\)

妖怪都是些比较喜欢安静的家伙,所以它们并不希望和很多妖怪相邻。所以这个树所有顶点的度数都小于或等于 \(3\)。妖怪和人一样,兴趣点随着年龄的变化自然就会变化,比如我们的\(18\) 岁少女幽香和八云紫就比较喜欢可爱的东西。幽香通过研究发现,基本上妖怪的兴趣只跟年龄有关,所以幽香打算选择一个地方\(u\)\(u\) 为编号),然后在\(u\)开一家面向年龄在 \(L\)\(R\) 之间(即年龄大于等于 \(L\) 小于等于 \(R\) )的妖怪的店。

也有可能 \(u\) 这个地方离这些妖怪比较远,于是幽香就想要知道所有年龄在 \(L\)\(R\) 之间的妖怪,到点 \(u\) 的距离的和是多少(妖怪到 \(u\) 的距离是该妖怪所在地方到 \(u\) 的路径上的边的权之和),幽香把这个称为这个开店方案的方便值。

幽香她们还没有决定要把店开在哪里,八云紫倒是准备了很多方案,于是幽香想要知道,对于每个方案,方便值是多少呢。

输入输出格式

输入格式:

第一行三个用空格分开的数 \(n,Q\)\(A\) ,表示树的大小、开店的方案个数和妖怪的年龄上限。

第二行 \(n\) 个用空格分开的数\(x_1,x_2,\ldots,x_n;x_i\) 表示第 \(i\) 个地点妖怪的年龄,满足\(0\le x_i\lt A\) 。(年龄是可以为\(0\)的,例如刚出生的妖怪的年龄为\(0\) 。)

接下来 \(n-1\) 行,每行三个用空格分开的数 \(a\)\(b\)\(c\) ,表示树上的顶点 \(a\)\(b\) 之间有一条权为\(c(1\le c\le1000)\)的边,\(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)\)

输出格式:

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

说明

满足\(n\le1.5\times10^5,Q\le2\times10^5\) 。对于所有数据,满足 \(A\le10^9\)


动态点分=sb码农题+sb卡常题,以上...

要相信同样是两个\(\log\)的算法,有人是\(2s\),有人是\(30s\)

先把点分树弄出来,然后对每个点维护自己在点分树上的子树的每个点到点分树父亲的距离和,最开始我拿的线段树,因为要按年龄划分。

然后预处理是\(O(n\log^2n)\),查询是\(O(\log^2n)\)

预处理按照点分治那样处理就可以了。

查询暴力向上跳,每次统计一下分治树外的答案+分治树外满足要求的点的个数乘上路径长度。

然后\(ST\)表的一个数组开小了,检查了快\(2h...\),然后卡常也卡不动,本地开\(10s\)才能勉强跑过去。注意这里年龄要先离散化,不然线段树开不下。

考虑没有查询可以直接前缀和,统计的时候拿\(vector\)存一下前缀和然后差分进行询问。询问的时候需要二分一下但是常数就小很多了。

然后咕咕不吸氧还是过不去...


Code:

// luogu-judger-enable-o2
#include <cstdio>
#include <vector>
#include <cctype>
#include <algorithm>
#define ll long long
const int N=150010;
using std::min;
using std::max;
int head[N],to[N<<1],Next[N<<1],edge[N<<1],cnt;
void add(int u,int v,int w)
{
    to[++cnt]=v,Next[cnt]=head[u],edge[cnt]=w,head[u]=cnt;
}
char c;int xx;
int read()
{
    c=getchar();xx=0;
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) {xx=xx*10+c-'0';c=getchar();}
    return xx;
}
namespace RMQLCA
{
    int dfn[N],st[N<<1][20],dep[N],Log[N<<1],dfs_clock;ll dis[N];
    void dfs(int now,int fa)
    {
        dep[now]=dep[fa]+1;
        st[dfn[now]=++dfs_clock][0]=now;
        for(int v,i=head[now];i;i=Next[i])
            if((v=to[i])!=fa)
                dis[v]=dis[now]+edge[i],dfs(v,now),st[++dfs_clock][0]=now;
    }
    void init()
    {
        dfs(1,0);
        for(int i=2;i<=dfs_clock;i++) Log[i]=Log[i>>1]+1;
        for(int j=1;j<=19;j++)
        {
            for(int x,y,i=1;i<=dfs_clock-(1<<j)+1;i++)
            {
                x=st[i][j-1],y=st[i+(1<<j-1)][j-1];
                st[i][j]=dep[x]<dep[y]?x:y;
            }
        }
    }
    ll getdis(int x,int y)
    {
        ll ret=dis[x]+dis[y];
        x=dfn[x],y=dfn[y];
        if(x>y) std::swap(x,y);
        int d=Log[y+1-x];
        x=st[x][d],y=st[y-(1<<d)+1][d];
        return ret-(dis[dep[x]<dep[y]?x:y]<<1);
    }
}
struct node
{
    int age,ct;ll f;
    bool friend operator <(node n1,node n2){return n1.age<n2.age;}
};
std::vector <node> sum[N];
std::vector <node>::iterator it;
ll su1=0,su2=0;
void query0(int now,int R)
{
    it=std::upper_bound(sum[now].begin(),sum[now].end(),(node){R,0,0});
    if(it!=sum[now].begin())
        it--,su1=it->f,su2=it->ct;
    else
        su1=su2=0;
}
int mi,rt,si,siz[N],del[N],par[N];
int n,q,m,poi[N],b[N];ll maxn;
void dfs1(int now,int fa)
{
    siz[now]=1;int mx=0;
    for(int v,i=head[now];i;i=Next[i])
        if(!del[v=to[i]]&&v!=fa)
            dfs1(v,now),siz[now]+=siz[v],mx=mx>siz[v]?mx:siz[v];
    mx=mx>si-siz[now]?mx:si-siz[now];
    if(mi>mx) mi=mx,rt=now;
}
void dfs2(int now,int fa,int w,ll dis)
{
    sum[w].push_back((node){poi[now],0,dis});
    for(int v,i=head[now];i;i=Next[i])
        if(!del[v=to[i]]&&v!=fa)
            dfs2(v,now,w,dis+edge[i]);
}
std::vector <int> Edge[N];
void deal(int now)
{
    std::sort(sum[now].begin(),sum[now].end());
    sum[now][0].ct=1;
    for(int i=1;i<sum[now].size();i++)
        sum[now][i].f+=sum[now][i-1].f,sum[now][i].ct=i+1;
}
void divide(int now)
{
    del[now]=1;
    for(int w,v,i=head[now];i;i=Next[i])
    {
        if(!del[w=to[i]])
        {
            mi=N,si=siz[w],dfs1(w,0);
            dfs2(w,0,v=rt,edge[i]);
            deal(v);
            par[v]=now,Edge[now].push_back(v);
            divide(v);
        }
    }
}
void getsum(int now,int v,int R,ll &ret1,ll &ret2)
{
    ret1=ret2=0;
    for(int w,i=0;i<Edge[now].size();i++)
        if((w=Edge[now][i])!=v)
            su1=su2=0,query0(w,R),ret1+=su1,ret2+=su2;
    ret2+=poi[now]<=R;
}
ll query(int now,int R)
{
    int las=now;ll ret1,ret2;
    getsum(now,0,R,ret1,ret2);
    ll ret=ret1;
    while(par[now])
    {
        getsum(par[now],now,R,ret1,ret2);
        ret+=ret1;
        ret+=ret2*RMQLCA::getdis(las,par[now]);
        now=par[now];
    }
    return ret;
}
int main()
{
    //freopen("data.in","r",stdin);
    //freopen("dew.out","w",stdout);
    n=read(),q=read(),maxn=read();
    for(int i=1;i<=n;i++) poi[i]=read();
    for(int u,v,w,i=1;i<n;i++)
    {
        u=read(),v=read(),w=read();
        add(u,v,w),add(v,u,w);
    }
    RMQLCA::init();
    mi=N,si=n;dfs1(1,0),divide(rt);
    ll lastans=0;
    for(int u,l,r,i=1;i<=q;i++)
    {
        u=read(),l=read(),r=read();
        l=(l+lastans)%maxn,r=(r+lastans)%maxn;
        if(l>r) std::swap(l,r);
        printf("%lld\n",lastans=(query(u,r)-query(u,l-1)));
    }
    return 0;
}

2018.12.6

posted @ 2018-12-06 11:02  露迭月  阅读(215)  评论(1编辑  收藏  举报