zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    gulp使用技巧-删除node_modules文件夹,解决目录层次太深删除报错的问题
    PHP学习-链接数据库
    教程笔记《JavaScript深入浅出》
    读书笔记《高性能网站建设指南》之雅虎军规
    CSS3边框图片-像素虚边的问题
    WebStorm设置手机测试服务器-局域网内其他设备访问
    gulp的安装和使用
    H5canvas赛车游戏-基于lufylegend引擎
    WebStorm设置左侧菜单栏背景色和样式
    基于jquery的-获取短信验证码-倒计时
  • 原文地址:https://www.cnblogs.com/mmlz/p/4442630.html
Copyright © 2011-2022 走看看