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 \rbrack\)的方案的方便值是多少”。对于其中第\(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 \rbrack\),我们询问\(u\)。我们枚举重心树内\(u\)的\(father\)链上的点\(now\),则\(now\)对答案的贡献为
\]
但是如果\(now\)仍然有\(father\),那么计算\(father_{now}\)的时候贡献就会计算重复。因此我们就可以在\(now\)时将重复的部分减除,这时\(tree_{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\n",ans);
}
fclose(stdin); fclose(stdout);
return 0;
}
BZOJ 4011 开店的更多相关文章
- BZOJ 4011: [HNOI2015]落忆枫音( dp )
DAG上有个环, 先按DAG计数(所有节点入度的乘积), 然后再减去按拓扑序dp求出的不合法方案数(形成环的方案数). ---------------------------------------- ...
- BZOJ 4011 HNOI2015 落忆枫音 DAG上的dp(实际上重点在于分析)
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4011 题意概述:给出一张N点的DAG(从1可以到达所有的点),点1的入度为0.现在加一条原 ...
- BZOJ 4011 HNOI2015 落忆枫音
AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=4011 题目很长,写得也很有诗意与浪漫色彩,让我们不禁感叹出题人是一个多么英俊潇洒的人. 所 ...
- BZOJ 4011 落忆枫音
几个重点: 1.从每个点任选一条入边,都可以成为一个树形图. 2.于是考虑所有答案减去有环的答案. 3.将要求的东西形式化表示出来,然后发现可以直接dp.. 好厉害啊.. #include<io ...
- bzoj 4011
看了好多篇题解才看懂的题,我实在太菜了... 首先根据一个我不知道的算法,可以证明在没有加入新的边的时候,原图的所有生成树的方案数就是所有点(除1以外)的度之积 那么在新加入这条边之后,我们仍然可以这 ...
- BZOJ 4011 【HNOI2015】 落忆枫音
题目链接:落忆枫音 以下内容参考PoPoQQQ大爷的博客 首先我们先来考虑一下如果没有新加入的那条边,答案怎么算. 由于这是一个\(DAG\),所以我们给每个点随便选择一条入边,最后一定会构成一个树形 ...
- BZOJ 4011: [HNOI2015]落忆枫音 计数 + 拓扑排序
Description 「恒逸,你相信灵魂的存在吗?」 郭恒逸和姚枫茜漫步在枫音乡的街道上.望着漫天飞舞的红枫,枫茜突然问出 这样一个问题. 「相信吧.不然我们是什么,一团肉吗?要不是有灵魂……我们 ...
- Week One
2018.11.21: 1.[BZOJ 4868][SHOI 2017] 从后往前枚举最后位置即可,如果$A<B$,用尽可能多的$A$替换$B$操作 Tip:很大的$C$可能爆$longlong ...
- 【BZOJ】【4011】【HNOI2015】落忆枫音
拓扑排序+DP 题解:http://blog.csdn.net/PoPoQQQ/article/details/45194103 http://www.cnblogs.com/mmlz/p/44487 ...
随机推荐
- 从零開始开发Android版2048 (一)初始化界面
自学Android一个月多了,一直在工作之余零零散散地看一些东西.感觉经常使用的东西都有些了解了,可是一開始写代码总会出各种奇葩的问题.感觉还是代码写得太少.这样继续杂乱地学习下去进度也太慢了,并且学 ...
- Windows与Linux下文件操作监控的实现
一.需求分析: 随着渲染业务的不断进行,数据传输渐渐成为影响业务时间最大的因素.究其原因就是因为数据传输耗费较长的时间.于是,依托于渲染业务的网盘开发逐渐成为迫切需要解决的需求.该网盘的实现和当前市场 ...
- App升级时数据库的迁移更新
前一段时间在进行App升级的时候,由于一开始版本初期没有考虑完善,导致走了很多弯路,后来经过自己的一些思考,总结出了一些在app升级的时候,数据库内文件同步保持更新的经验,希望能给大家带来帮助. 总体 ...
- Android(java)学习笔记184:生成 4种 不同权限的文件
1.首先我们编写一个生成 4种 不同权限的文件的程序案例: (1)首先是activity_main.xml文件: <RelativeLayout xmlns:android="http ...
- 【原创教程】JavaScript详解之语法和对象
JavaScript的好的想法:函数.弱类型.动态对象和一个富有表现力的对象字面量表示法. JavaScript的坏的想法:基于全局变量的编程模型. 好了,不管他是好的还是坏的,都是我的最爱,下面 ...
- WisDom.Net 框架设计(八) 持久层
WisDom.Net ---持久层 1.什么是持久层 持久层负责最基础的功能支撑,为项目提供一个高层,统一,和并发的数据持久机制,提供了比如建立数据库连接,关闭数据库连接,执行sql语 ...
- C# 展开和折叠代码的快捷键
C# 展开和折叠代码的快捷键 VS2005代码编辑器的展开和折叠代码确实很方便和实用.以下是展开代码和折叠代码所用到的快捷键,很常用: Ctrl + M + O: 折叠所有方法 Ctrl + M + ...
- [上传下载] C#FileDown文件下载类 (转载)
点击下载 FileDown.zip 主要功能如下 .参数为虚拟路径 .获取物理地址 .普通下载 .分块下载 .输出硬盘文件,提供下载 支持大文件.续传.速度限制.资源占用小 看下面代码吧 /// &l ...
- WinEdt打开UTF-8文件乱码问题——ctex[转]
原来这么简单,mark一下! [转自:http://fstang.diandian.com/post/2012-04-17/40030401020] 其实这个问题网上文章已经有一大堆了...我只是记录 ...
- Java--获取request中所有参数的方法
我们通常用到request获取某个参数的方法: String value=request.getparameter("key"); 如果想要获取request中所有的参数呢? re ...