BZOJ 4011 开店

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,cdots,x_{n},x_{i})表示第(i)个地点妖怪的年龄,满足(0 le x_{i} < A)。(年龄是可以为(0)的,例如刚出生的妖怪的年龄为(0)。) 接下来(n-1)行,每行三个用空格分开的数(a,b,c),表示树上的顶点(a)(b)之间有一条权为(c)((1 le c le 1000))的边,(a)(b)是顶点编号。 接下来(Q)行,每行三个用空格分开的数(u,a,b)。对于这(Q)行的每一行,用(a,b,A)计算出(L)(R),表示询问“在地方(u)开店,面向妖怪的年龄区间为(lbrack L,R brack)的方案的方便值是多少”。对于其中第(1)行,(L)(R)的计算方法为:(L=min(a ; mod ; A,b ; mod ; A), R=max(a ; mod ; A,b ; mod ; A))。对于第(2)到第(Q)行,假设前一行得到的方便值为(ans),那么当前行的(L)(R)计算方法为: (L=min((a+ans) ; mod ; A,(b+ans) ; mod ; A), R=max((a+ans) ; mod ; A,(b+ans) ; mod ; 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 le 150000,Q le 200000)。对于所有数据,满足(A le 10^{9})

又一道动态树分治的题目。考场上妙想码了(2h),最后被卡常数,没开long long被卡成了暴力分。呵呵TAT。。。
(x)离散化后,我们构建重心树(树分治),然后每个节点对其重心子树维护(4)个数组——(element,kind,tree_{0},tree_{1})
(element_{i})表示(i)的重心子树都有那些(x)(排了序),(kind_{i})表示(i)的重心子树中前(i)种存在的(x)的个数和。(tree_{0})表示以(i)为根的重心子树前(i)种存在的(x)元素,在原树中到(i)的距离和,(tree_{1})表示以表示以(i)为根的重心子树前(i)种存在的(x)元素,在原树中到(father_{i})(i)在重心树中的父亲)的距离和。
算贡献需要用到容斥,利用树分治的思想,我们需要将在同一棵子树内的贡献减去。
对于某个区间(lbrack L,R brack),我们询问(u)。我们枚举重心树内(u)(father)链上的点(now),则(now)对答案的贡献为

[dis_{now,u} imes(kind_{now,r}-kind_{now,l-1})+tree_{0,now,r}-tree_{0,now,l-1} ]

但是如果(now)仍然有(father),那么计算(father_{now})的时候贡献就会计算重复。因此我们就可以在(now)时将重复的部分减除,这时(tree_{1})就有用了。这时贡献为

[-(dis_{father_{now},u} imes(kind_{now,r}-kind_{now,l-1})+tree_{1,now,r}-tree_{1,now,l-1}) ]

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<vector>
using namespace std;

typedef long long ll;
#define rhl (4000037)
#define inf (1<<30)
#define maxn (150010)
int N,Q,A,x[maxn],disc[maxn],tot,size[maxn],large[maxn];
int dis[maxn],ge[maxn],num,have[maxn],be1[maxn],be2[maxn],be3[2][maxn];
int side[maxn],toit[maxn*2],next[maxn*2],cost[maxn*2];
int cnt,arr[maxn],best,father[maxn],last1,last2,last3[2],sz[maxn];
ll sum[maxn],ans; bool vis[maxn],in[maxn];
int element[maxn*20],kind[maxn*20]; ll tree[2][maxn*20];
struct node { int a,b,c; }edge[rhl];

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(int a,int b,int c) { next[++cnt] = side[a]; side[a] = cnt; toit[cnt] = b; cost[cnt] = c; }
inline void ins(int a,int b,int c) { add(a,b,c); add(b,a,c); }

inline void insert(int a,int b,int c)
{
	if (a > b) swap(a,b);
	int now = (a*233+b*23)%rhl;
	while (edge[now].a) { ++now; if (now >= rhl) now -= rhl; }
	edge[now] = (node){a,b,c};
}
inline int find(int a,int b)
{
	if (a > b) swap(a,b);
	int now = (a*233+b*23)%rhl;
	while (edge[now].a!=a||edge[now].b!=b) { ++now; if (now >= rhl) now -= rhl; }
	return edge[now].c;
}

inline void getroot(int now,int fa,int rest)
{
	size[now] = 1; large[now] = 0;
	for (int i = side[now];i;i = next[i])
	{
		if (toit[i] == fa||vis[toit[i]]) continue;
		getroot(toit[i],now,rest);
		size[now] += size[toit[i]];
		large[now] = max(large[now],size[toit[i]]);
	}
	large[now] = max(large[now],rest-size[now]);
	if (large[now] < large[best]) best = now;
}
inline int find_root(int now,int rest) { best = 0; getroot(now,0,rest); return best; }

inline void dfs(int st,int now,int fa)
{
	if (st) insert(st,now,dis[now]);
	if (!in[x[now]]) in[x[now]] = true,arr[++num] = x[now];
	ge[x[now]]++; sum[x[now]] += (ll)dis[now];
	size[now] = 1;
	for (int i = side[now];i;i = next[i])
	{
		if (toit[i] == fa||vis[toit[i]]) continue;
		dis[toit[i]] = dis[now]+cost[i];
		dfs(st,toit[i],now);
		size[now] += size[toit[i]];
	}
}
inline void calc(int st,int now,int len,bool sign,int root)
{
	dis[now] = len; dfs(st,now,0);
	sort(arr+1,arr+num+1);
	if (st)
	{
		sz[root] = num;
		be1[root] = last1+1;
		for (int i = 1;i <= num;++i) element[++last1]=arr[i];
		be2[root] = ++last2+1;
	}
	be3[sign][root] = ++last3[sign]+1;
	for (int i = 1;i <= num;++i)
	{
		if (st) ++last2,kind[last2] = kind[last2-1]+ge[arr[i]];
		++last3[sign]; tree[sign][last3[sign]] = tree[sign][last3[sign]-1]+sum[arr[i]];
		in[arr[i]] = false; ge[arr[i]] = sum[arr[i]] = 0;
	}
	num = 0;
}

inline void cut(int now)
{
	calc(now,now,0,0,now);
	vis[now] = true;
	for (int i = side[now];i;i = next[i])
	{
		if (vis[toit[i]]) continue;
		int root = find_root(toit[i],size[toit[i]]);
		father[root] = now;
		calc(0,toit[i],cost[i],1,root);
		cut(root);
	}
}

inline int lb(int lll,int rrr,int key)
{
	int l = lll,r = rrr;
	while (l <= r)
	{
		int mid = (l + r) >> 1;
		if (element[mid] < key) l = mid + 1;
		else r = mid - 1;
	}
	return l-lll+1;
}

inline int ub(int lll,int rrr,int key)
{
	int l = lll,r = rrr;
	while (l <= r)
	{
		int mid = (l + r) >> 1;
		if (element[mid] <= key) l = mid + 1;
		else r = mid - 1;
	}
	return l-lll+1;
}

inline ll query(int u,int L,int R)
{
	ll ret = 0;
	for (int now = u;now;now = father[now])
	{
		int l = lb(be1[now],be1[now]+sz[now]-1,L),r = ub(be1[now],be1[now]+sz[now]-1,R)-1;
		
		int all = kind[be2[now]+r-1]-kind[be2[now]+l-2];
		ll inc = tree[0][be3[0][now]+r-1]-tree[0][be3[0][now]+l-2];
		ret += (ll)all*find(now,u)+inc;
		if (father[now])
		{
			inc = tree[1][be3[1][now]+r-1]-tree[1][be3[1][now]+l-2];
			ret -= (ll)all*find(father[now],u)+inc;
		}
	}
	return ret;
}

int main()
{
	freopen("4012.in","r",stdin);
	freopen("4012.out","w",stdout);
	N = read(),Q = read(),A = read();
	for (int i = 1;i <= N;++i) disc[++tot] = x[i] = read();
	sort(disc+1,disc+tot+1); tot = unique(disc+1,disc+tot+1)-disc-1;
	for (int i = 1;i <= N;++i) x[i] = lower_bound(disc+1,disc+tot+1,x[i])-disc;
	for (int i = 1,a,b,c;i < N;++i) a = read(),b = read(),c = read(),ins(a,b,c);
	large[0] = inf;
	cut(find_root(1,N));
	while (Q--)
	{
		int u = read(),L,R; ll a = read(),b = read();
		L = min((ans + a)%A,(ans + b)%A),R = max((ans + a)%A,(ans + b)%A);
		L = lower_bound(disc+1,disc+tot+1,L)-disc,R = upper_bound(disc+1,disc+tot+1,R)-disc-1;
		ans = query(u,L,R); printf("%lld
",ans);
	}
	fclose(stdin); fclose(stdout);
	return 0;
}
原文地址:https://www.cnblogs.com/mmlz/p/4442630.html