您的位置:首页 > 其它

BZOJ 4012: [HNOI2015]开店 -- 动态树分治

2017-03-11 16:43 344 查看

4012: [HNOI2015]开店

Time Limit: 70 Sec  Memory Limit: 512 MB
Submit: 1463  Solved: 635

[Submit][Status][Discuss]

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

Source

总算搞掉了一直挂在心里的题。。。

被题面吸引进来,跪着也要切掉。

构建好重心树后每个点维护:

以这个点为重心的块内年龄区间的距离和以及数量

以这个点为根到她到重心树上父亲的年龄区间的距离和以及数量

对于每个查询,在重心树上跳同时记录跳的距离,答案额外加上距离*该块内合法妖怪数量。

但是如果每个点建两棵线段树的话,就算动态开点,空间复杂度也是吃不消的。

因为仅需要支持区间求和,可以在建树时开两个vector,按年龄排序并处理前缀和,查询时二分即可。// sro PoPoQQQ orz

总复杂度O(nlog^2n+qlog^2n)

//另hzwer的点分治写法可以被卡,,不能保证重心树深度严格<= logn

代码丑死了(摔

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
#define debug(x) cout<<#x<<"="<<x<<endl
const int maxn = 300009;
int b[maxn],a[maxn],W[maxn],siz[maxn];
int first[maxn];
bool vis[maxn];
struct edg
{
int next;
int to;
int val;
}e[maxn<<1];
int e_sum;
struct Info{int anc,idx;ll dis;};
int n,m,A,tot,cnt,rt,sum;
vector<Info> List[maxn];
vector<pair<int,ll> > Mast[maxn],root[maxn];
int c1[maxn],c2[maxn];
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
inline void add_edg(int x,int y,int z)
{
e_sum++;
e[e_sum].next=first[x];
first[x]=e_sum;
e[e_sum].to=y;
e[e_sum].val=z;
}
inline int Lower(int x)
{
int l=1,r=b[0],pos=b[0]+1;
while(l<=r)
{
int mid=(l+r)>>1;
if(b[mid]>=x){pos=mid;r=mid-1;}
else l=mid+1;
}return pos;
}
inline int Upper(int x)
{
int l=1,r=b[0],pos=0;
while(l<=r)
{
int mid=(l+r)>>1;
if(b[mid]<=x){pos=mid;l=mid+1;}
else r=mid-1;
}return pos;
}
pair<ll,int> ask_root(int idx,int left,int right)
{
int l=0,r=c2[idx],L=0,R=0;
while(l<=r)
{
int mid=(l+r)>>1;
if(root[idx][mid].first<=left){L=mid;l=mid+1;}
else r=mid-1;
}
l=0,r=c2[idx];
while(l<=r)
{
int mid=(l+r)>>1;
if(root[idx][mid].first<=right){R=mid;l=mid+1;}
else r=mid-1;
}
return make_pair(root[idx][R].second-root[idx][L].second,R-L);
}
pair<ll,int> ask_Mast(int idx,int left,int right)
{
int l=0,r=c1[idx],L=0,R=0;
while(l<=r)
{
int mid=(l+r)>>1;
if(Mast[idx][mid].first<=left){L=mid;l=mid+1;}
else r=mid-1;
}
l=0,r=c1[idx];
while(l<=r)
{
int mid=(l+r)>>1;
if(Mast[idx][mid].first<=right){R=mid;l=mid+1;}
else r=mid-1;
}
return make_pair(Mast[idx][R].second-Mast[idx][L].second,R-L);
}
void find(int x,int fa)
{
W[x]=0;siz[x]=1;
for(int i=first[x];i;i=e[i].next)
{
int w=e[i].to;
if(w==fa||vis[w]) continue;
find(w,x);
W[x]=max(W[x],siz[w]);
siz[x]+=siz[w];
}W[x]=max(W[x],sum-siz[x]);
if(W[x]<W[rt]) rt=x;
}
void calc(int x,int fa)
{
siz[x]=1;
for(int i=first[x];i;i=e[i].next)
{
int w=e[i].to;
if(w==fa||vis[w]) continue;
calc(w,x);
siz[x]+=siz[w];
}
}
void dfs(int x,int fa,int dis)
{
List[x].push_back((Info){rt,cnt,dis});
Mast[rt].push_back(make_pair(a[x],dis));c1[rt]++;
root[cnt].push_back(make_pair(a[x],dis));c2[cnt]++;
for(int i=first[x];i;i=e[i].next)
{
int w=e[i].to;
if(w==fa||vis[w]) continue;
dfs(w,x,dis+e[i].val);
}
}
void solve(int x)
{
vis[x]=1;
for(int i=first[x];i;i=e[i].next)
{
int w=e[i].to;
if(vis[w]) continue;
cnt++;dfs(w,x,e[i].val);
root[cnt].push_back(make_pair(0,0));
sort(root[cnt].begin(),root[cnt].end());
for(int i=1;i<root[cnt].size();i++) root[cnt][i].second+=root[cnt][i-1].second;
}
Mast[x].push_back(make_pair(0,0));sort(Mast[x].begin(),Mast[x].end());
for(int i=1;i<Mast[x].size();i++) Mast[x][i].second+=Mast[x][i-1].second;
for(int i=first[x];i;i=e[i].next)
{
int w=e[i].to;
if(vis[w]) continue;
rt=0;calc(w,x);sum=siz[w];
find(w,0);solve(rt);
}
}
ll query(int x,int l,int r)
{
if(l>r) return 0;
ll ans=ask_Mast(x,l-1,r).first,sz=List[x].size();
for(int i=0;i<sz;i++)
{
if(l<=a[List[x][i].anc]&&a[List[x][i].anc]<=r) ans+=List[x][i].dis;
pair<ll,int> L=ask_Mast(List[x][i].anc,l-1,r),R=ask_root(List[x][i].idx,l-1,r);
ans+=L.first+L.second*List[x][i].dis;
ans-=R.first+R.second*List[x][i].dis;
}return ans;
}
int main()
{
n=read();m=read();A=read();
for(int i=1;i<=n;i++) a[i]=b[i]=read();
sort(b+1,b+1+n);b[0]=unique(b+1,b+1+n)-b-1;
for(int i=1;i<=n;i++) a[i]=Lower(a[i]);
for(int i=1;i<n;i++)
{
int x=read(),y=read(),z=read();
add_edg(x,y,z);add_edg(y,x,z);
}rt=0;W[0]=n+1;sum=n;
find(1,0);solve(rt);
ll last=0;
int x;ll L,R,l,r;
for(int i=1;i<=m;i++)
{
x=read();L=read(),R=read();
l=min((L+last)%A,(R+last)%A);r=max((L+last)%A,(R+last)%A);
printf("%lld\n",last=query(x,Lower((int)l),Upper((int)r)));
last%=(ll)A;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  点分治