zoukankan      html  css  js  c++  java
  • bzoj 4012: [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 的路径上的边的权之和) ,幽香把这个
    称为这个开店方案的方便值。幽香她们还没有决定要把店开在哪里,八云紫倒是准
    备了很多方案,于是幽香想要知道,对于每个方案,方便值是多少呢。

    解题报告:
    前情:本人的只是水过.
    我们知道:(dis[u,v]=d[u]+d[v]-2*d[lca(u,v)])
    对于前两项,我们是已知的,对于(d[lca(u,v)])的求法十分有意思,其实就是u和v到1的路径的交,这样,我们就可以先用所有的v,将v到1的路径打上标记,然后统计答案时就是u到1的路径上的标记和,用树链剖分即可(logn)的标记和查询,然后关于年龄限制,我们只需将树链剖分的线段树可持久化一下即可.

    一些注意的地方:
    一开始标记打错,应该标记上记录的是经过次数而不是边权×次数,然后就是区间修改主席树的基本操作了.

    #include <algorithm>
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #define RG register
    #define il inline
    #define iter iterator
    #define Max(a,b) ((a)>(b)?(a):(b))
    #define Min(a,b) ((a)<(b)?(a):(b))
    using namespace std;
    typedef long long ll;
    const int N=150005;
    int head[N],num=0,nxt[N<<1],to[N<<1],dis[N<<1],n,Q,A,edg[N],tot,val[N];
    int bt=0,b[N],a[N];ll sum[N],w[N],sed[N];
    int gi(){
    	int str=0;char ch=getchar();
    	while(ch>'9' || ch<'0')ch=getchar();
    	while(ch>='0' && ch<='9')str=(str<<1)+(str<<3)+ch-48,ch=getchar();
    	return str;
    }
    void link(int x,int y,int z){nxt[++num]=head[x];to[num]=y;dis[num]=z;head[x]=num;}
    bool comp(int i,int j){return edg[i]<edg[j];}
    int ids=0,fa[N],top[N],id[N],son[N],sz[N];ll path[N];
    void dfs1(int x){
    	int u;
    	sz[x]=1;
    	for(int i=head[x];i;i=nxt[i]){
    		u=to[i];if(sz[u])continue;
    		path[u]=path[x]+dis[i];fa[u]=x;
    		val[u]=dis[i];dfs1(u);sz[x]+=sz[u];
    		if(sz[u]>sz[son[x]])son[x]=u;
    	}
    }
    void dfs2(int x,int tp){
    	id[x]=++ids;top[x]=tp;
    	if(son[x])dfs2(son[x],tp);int u;
    	for(int i=head[x];i;i=nxt[i]){
    		u=to[i];if(u==fa[x] || u==son[x])continue;
    		dfs2(u,u);
    	}
    }
    int root[N],totnode=0;
    struct node{
    	int lazy,l,r;ll sum;
    }tr[N*30];
    void upd(int rt,int l,int r){
    	tr[rt].sum=tr[tr[rt].l].sum+tr[tr[rt].r].sum+tr[rt].lazy*(w[r]-w[l-1]);
    }
    void updata(int &rt,int last,int l,int r,int sa,int se){
    	rt=++totnode;tr[rt]=tr[last];
    	if(sa==l && r==se){
    		tr[rt].sum+=w[r]-w[l-1];
    		tr[rt].lazy++;
    		return ;
    	}
    	int mid=(l+r)>>1;
    	if(se<=mid)updata(tr[rt].l,tr[last].l,l,mid,sa,se);
    	else if(sa>mid)updata(tr[rt].r,tr[last].r,mid+1,r,sa,se);
    	else{
    		updata(tr[rt].l,tr[last].l,l,mid,sa,mid);
    		updata(tr[rt].r,tr[last].r,mid+1,r,mid+1,se);
    	}
    	upd(rt,l,r);
    }
    void updpath(int x){
    	int rt=edg[x];
    	while(x){
    		updata(root[rt],root[rt],1,n,id[top[x]],id[x]);
    		x=fa[top[x]];
    	}
    }
    void solve(){
    	sort(a+1,a+n+1,comp);
       dfs1(1);dfs2(1,1);
    	for(int i=1;i<=n;i++)w[id[i]]=val[i],sed[edg[i]]+=path[i];
    	for(int i=1;i<=n;i++)w[i]+=w[i-1],sed[i]+=sed[i-1];
    	for(int j=1,i;j<=n;j++){
    		i=a[j];
    		if(!root[edg[i]])
    			root[edg[i]]=root[edg[i]-1];
    		updpath(i);
    	}
    }
    ll query(int rt,int l,int r,int sa,int se,int mr){
    	if(l==sa && r==se)return tr[rt].sum+(ll)mr*(w[r]-w[l-1]);
    	int mid=(l+r)>>1;ll ret=0;mr+=tr[rt].lazy;
    	if(se<=mid)ret=query(tr[rt].l,l,mid,sa,se,mr);
    	else if(sa>mid)ret=query(tr[rt].r,mid+1,r,sa,se,mr);
    	else{
    		ret+=query(tr[rt].l,l,mid,sa,mid,mr);
    		ret+=query(tr[rt].r,mid+1,r,mid+1,se,mr);
    	}
    	return ret;
    }
    ll qtree(int x,int fr){
    	if(fr==0)return 0;
    	ll ret=0;int rt=root[fr];
    	while(x){
    		ret+=query(rt,1,n,id[top[x]],id[x],0);
    		x=fa[top[x]];
    	}
    	return ret;
    }
    void settle(){
       ll ret=0,ans=0,u,x,y,xa,xb;
    	while(Q--){
    		u=gi();xa=gi();xb=gi();
    		x=Min((xa+ans)%A,(xb+ans)%A);
    		y=Max((xa+ans)%A,(xb+ans)%A);
    		x=lower_bound(b+1,b+tot+1,x)-b;
    		y=upper_bound(b+1,b+tot+1,y)-b-1;
    		ret=(ll)(sum[y]-sum[x-1])*path[u];
    		ret+=sed[y]-sed[x-1];
    		ret-=(ll)(qtree(u,y)-qtree(u,x-1))<<1;
    		ans=ret;
    		printf("%lld
    ",ans);
    	}
    }
    void work()
    {
    	n=gi();Q=gi();A=gi();
    	for(int i=1;i<=n;i++)edg[i]=gi(),b[++bt]=edg[i],a[i]=i;
    	int x,y,z;
    	for(int i=1;i<n;i++){
    		x=gi();y=gi();z=gi();
    		link(x,y,z);link(y,x,z);
    	}
    	sort(b+1,b+bt+1);
    	tot=unique(b+1,b+bt+1)-b-1;
    	for(int i=1;i<=n;i++)edg[i]=lower_bound(b+1,b+tot+1,edg[i])-b;
    	for(int i=1;i<=n;i++)sum[edg[i]]++;
    	for(int i=1;i<=tot;i++)sum[i]+=sum[i-1];
    	solve();settle();
    }
    
    int main()
    {
    	work();
    	return 0;
    }
    
    
  • 相关阅读:
    python——scrapy的三种登录方式,下载中间件(Downloademiddleware),settings文件
    PHPExcel探索之旅---阶段四 导入文件
    PHPExcel探索之旅---阶段三 绘制图表
    PHPExcel探索之旅---阶段二 设置表格样式
    PHPExcel探索之旅---阶段一
    PDO中构建事务处理的应用程序
    对图片进行翻转
    用给定的角度旋转图像
    为背景图像添加图像水印(位置随机)
    对图片进行缩放
  • 原文地址:https://www.cnblogs.com/Yuzao/p/7505827.html
Copyright © 2011-2022 走看看