【牛客Wannafly挑战赛12】小H和圣诞树
可以考虑边分治,对于某一种颜色,我们处理出分治边左右两边所有以这个颜色为端点的路径长度,之后随便拼一拼就好了
但是这样对于每一组询问都需要边分一遍,这样做复杂度是\(O(nm+n\log n)\)的
还有一种更暴力的做法,就是枚举树上所有路径,这样就可以直接统计了,复杂度是\(O(n^2)\)的
把这两个暴力结合一下,当一个分治块大小小于\(\sqrt{n}\)的时候,我们就直接跑第二种暴力,否则我们就跑边分治
跑第二种暴力的时候我们需要快速判断当前这个点对对应哪一个询问,于是需要二分一下,于是有一个\(\log\),所以我们把阈值设得比\(\sqrt{n}\)稍小一些,使得复杂度向边分治倾斜,这样就能跑过去了
代码
#include<bits/stdc++.h>
#define re register
#define LL long long
#pragma GCC optimize(3)
#define max(a,b) ((a)>(b)?(a):(b))
inline int read() {
char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
const int maxn=1e5+5;const int M=4e5+3;
int rn,n,num,rt,Mnow,S,B,tot,Q,sz;
struct E{int v,nxt,w;}e[M<<1];
std::vector<int> son[M],w[M];
int vis[M],head[M],sum[M],col[maxn];
LL Ans[maxn],tax[2][maxn],b[maxn];
int st[2][M],top[2],t[2][maxn],c[maxn],qx[maxn],qy[maxn];
inline int find(LL x) {
int l=1,r=sz;
while(l<=r) {
int mid=l+r>>1;
if(b[mid]==x) return mid;
if(b[mid]<x) l=mid+1;else r=mid-1;
}
return 0;
}
inline void add(int x,int y,int w) {
e[++num].v=y;e[num].nxt=head[x];head[x]=num;e[num].w=w;
}
void dfs1(int x,int fa) {
for(re int i=head[x];i;i=e[i].nxt) {
if(e[i].v==fa) continue;
son[x].push_back(e[i].v);w[x].push_back(e[i].w);
dfs1(e[i].v,x);
}
}
void getrt(int x,int fa) {
sum[x]=1;
for(re int i=head[x];i;i=e[i].nxt) {
if(e[i].v==fa||vis[i>>1]) continue;
getrt(e[i].v,x);sum[x]+=sum[e[i].v];
int now=max(sum[x],S-sum[x]);
if(now<Mnow) Mnow=now,rt=i;
}
}
void dfs2(int x,LL dis,int fa,int o) {
if(x<=rn) tax[o][col[x]]+=dis,st[o][++top[o]]=col[x],t[o][col[x]]++;
for(re int i=head[x];i;i=e[i].nxt) {
if(e[i].v==fa||vis[i>>1]) continue;
dfs2(e[i].v,dis+e[i].w,x,o);
}
}
void calc(int x,int fa,int o,LL dis) {
if(x<=rn&&col[x]>=o) {
int to=c[find(1ll*o*rn+col[x])];
if(to) Ans[to]+=dis;
}
for(re int i=head[x];i;i=e[i].nxt) {
if(e[i].v==fa||vis[i>>1]) continue;
calc(e[i].v,x,o,dis+e[i].w);
}
}
void dfs(int x,int fa) {
if(x<=rn) calc(x,0,col[x],0);
for(re int i=head[x];i;i=e[i].nxt) {
if(e[i].v==fa||vis[i>>1]) continue;
dfs(e[i].v,x);
}
}
void solve(int x,int s) {
if(s<=B) {dfs(x,0);return;}
Mnow=M,S=s,getrt(x,0);
if(Mnow==M) return;vis[rt>>1]=1;
dfs2(e[rt].v,e[rt].w,0,0);dfs2(e[rt^1].v,0,0,1);
for(re int i=1;i<=Q;i++) {
Ans[i]+=1ll*t[0][qx[i]]*tax[1][qy[i]]+1ll*t[1][qx[i]]*tax[0][qy[i]]
+1ll*t[0][qy[i]]*tax[1][qx[i]]+1ll*t[1][qy[i]]*tax[0][qx[i]];
}
while(top[0]) tax[0][st[0][top[0]]]=t[0][st[0][top[0]]]=0,top[0]--;
while(top[1]) tax[1][st[1][top[1]]]=t[1][st[1][top[1]]]=0,top[1]--;
int k=e[rt^1].v,h=S-sum[e[rt].v];
solve(e[rt].v,sum[e[rt].v]);solve(k,h);
}
int main() {
rn=n=read();
for(re int i=1;i<=n;i++) col[i]=read();
for(re int x,y,z,i=1;i<n;i++)
x=read(),y=read(),z=read(),add(x,y,z),add(y,x,z);
dfs1(1,0);num=1;for(re int i=1;i<=n;i++) head[i]=0;int s[2];
for(re int i=1;i<=n;i++) {
int t=son[i].size();
if(t<=2) {
for(re int j=0;j<t;++j)
add(i,son[i][j],w[i][j]),add(son[i][j],i,w[i][j]);
continue;
}
s[0]=++n,s[1]=++n;
add(i,s[0],0),add(s[0],i,0);add(i,s[1],0),add(s[1],i,0);
for(re int j=0;j<t;++j)
son[s[j&1]].push_back(son[i][j]),w[s[j&1]].push_back(w[i][j]);
}
Q=read();B=0.8*std::sqrt(rn);
for(re int i=1;i<=Q;i++) {
qx[i]=read(),qy[i]=read();
if(qx[i]>qy[i]) std::swap(qx[i],qy[i]);
b[i]=1ll*qx[i]*rn+qy[i];
}
std::sort(b+1,b+Q+1);sz=std::unique(b+1,b+Q+1)-b-1;
for(re int i=Q;i;--i) c[find(1ll*qx[i]*rn+qy[i])]=i;
solve(1,n);
for(re int i=1;i<=Q;i++) {
int to=c[find(1ll*qx[i]*rn+qy[i])];
if(to!=i) {printf("%lld\n",Ans[to]);continue;}
Ans[i]/=(qx[i]==qy[i]?2ll:1);
printf("%lld\n",Ans[i]);
}
return 0;
}
【牛客Wannafly挑战赛12】小H和圣诞树的更多相关文章
- 【牛客Wannafly挑战赛12】 题解
传送门:https://www.nowcoder.com/acm/contest/79#question 说是比赛题解,其实我只会前三题: 后面的一定补 T1 题意,在一个长度为n的时间内,问如何选择 ...
- 牛客wannafly 挑战赛14 B 前缀查询(trie树上dfs序+线段树)
牛客wannafly 挑战赛14 B 前缀查询(trie树上dfs序+线段树) 链接:https://ac.nowcoder.com/acm/problem/15706 现在需要您来帮忙维护这个名册, ...
- 牛客~~wannafly挑战赛19~A 队列
链接:https://www.nowcoder.com/acm/contest/131/A来源:牛客网 题目描述 ZZT 创造了一个队列 Q.这个队列包含了 N 个元素,队列中的第 i 个元素用 Qi ...
- 牛客Wannafly挑战赛23 B.游戏
游戏 题目描述 小N和小O在玩游戏.他们面前放了n堆石子,第i堆石子一开始有ci颗石头.他们轮流从某堆石子中取石子,不能不取.最后无法操作的人就输了这个游戏.但他们觉得这样玩太无聊了,更新了一下规则. ...
- 牛客Wannafly挑战赛13-BJxc军训-费马小定理、分式取模、快速幂
参考:https://blog.csdn.net/qq_40513946/article/details/79839320 传送门:https://www.nowcoder.com/acm/conte ...
- 牛客 Wannafly 挑战赛26D 禁书目录 排列组合 概率期望
原文链接https://www.cnblogs.com/zhouzhendong/p/9781060.html 题目传送门 - NowCoder Wannafly 26D 题意 放一放这一题原先的题面 ...
- 牛客Wannafly挑战赛26E 蚂蚁开会(树链剖分+线段树)
传送门 题面描述 一颗n个节点的树,m次操作,有点权(该节点蚂蚁个数)和边权(相邻节点的距离). 三种操作: 操作1:1 i x将节点i的点权修改为x.(1 <= i <= n; 1 &l ...
- 牛客Wannafly挑战赛11E 白兔的刁难
传送门 如果大力推单位根反演就可以获得一个 \(k^2logn\) 的好方法 \[ans_{t}=\frac{1}{k}\sum_{i=0}^{k-1}(w_k^{-t})^i(w_k^i+1)^n\ ...
- 牛客Wannafly挑战赛23F 计数(循环卷积+拉格朗日插值/单位根反演)
传送门 直接的想法就是设 \(x^k\) 为边权,矩阵树定理一波后取出 \(x^{nk}\) 的系数即可 也就是求出模 \(x^k\) 意义下的循环卷积的常数项 考虑插值出最后多项式,类比 \(DFT ...
随机推荐
- Java 导出excel进行换行
在导出excel 的时候,如果原始文字中含有 \n 字符,生成的excel中 会生成 _0040_ 字样的乱码, 如果把 \n 替换为<br/>,excel不会识别成换行符 excel 认 ...
- sublime上插件的安装与使用
1.插件安装的方式 插件安装方式一:直接安装 下载插件安装包后,把安装包解压到packages目录(菜单->首选项->浏览插件目录)中,完成安装 插件安装方法二:使用package con ...
- NX二次开发-UFUN拉伸函数UF_MODL_create_extruded2
NX9+VS2012 //NX二次开发中常用拉伸函数为UF_MODL_create_extruded2,但是此函数不能拉伸片体, //想要拉伸片体用函数UF_MODL_create_extruded. ...
- c语言NULL和0区别及NULL详解
先看下面一段代码输出什么: #include<stdo.h> int main() { int *p=NULL; printf("%s",p); } 输出<n ...
- C++之string
一.常用操作 二.用“+”连接字符串的注意事项
- oracle11g 导出表报EXP-00011:table不存在。
oracle11g 导出表报EXP-00011:table不存在. oracle11g,在用exp命令备份数据库时,如果表中没有数据报EXP-00011错误,对应的表不存在.这导致对应的空表无法备份. ...
- 2019 牛客多校第一场 A Equivalent Prefixes
题目链接:https://ac.nowcoder.com/acm/contest/881/A 题目大意 定义 RMQ(u, L, R) 为 u 数组在区间 [L, R] 上最小值的下标. 如果有 2 ...
- 自动化测试工具1-testcomplete
TestComplete是SmartBear公司开发的一套支持自动测试软件的工具.在当今的软件开发中,自动测试非常重要,大型软件开发公司很久以来就已经将其作为软件开发的一项重要环节.然而,自动测试软件 ...
- java 8 lambda函数
1 为什么要引进lambda函数 可以简化编码,将事情更多的交给编译器,让编译器帮我们推断我们写的代码的完整形式. 2 lambda函数的语法 2.1 -> (arg1, arg2) -> ...
- Spring Boot + kkFileView-2.1.2 实现文档在线预览
1. 下载kkFileview:https://gitee.com/kekingcn/file-online-preview/releases 2. 启动服务 进入 bin 目录,双击 startup ...