【牛客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 ...
随机推荐
- Download ubuntu Linux
https://ubuntu.com/download/desktop
- java script两个列表之间移动数据
<select name="b1" id="hao" style="width:100px; height:200px;" size= ...
- 凑出和相等的k组数,玄学结论——hdu6616
[1,n]n个数分成k组,每组n/k个,问k组数和相等的解决方案 首先(1+n)*n/2判定一下是否可以被k整除 n/k为偶数时显然成立 n/k为奇数时每组数前三个很难配,我想了一种玄学的结论,也证明 ...
- php-fpm 服务
编译安装PHP的时候已经加入了--enable-fpm 在此基础上启动php-fpm服务 cp /usr/local/php/etc/php-fpm.conf.default /usr/local/p ...
- NX二次开发-UFUN输入Part的TAG,获取整个部件表达式的TAG和表达式个数UF_MODL_ask_exps_of_part
NX9+VS2012 #include <uf.h> #include <uf_modl.h> #include <uf_part.h> UF_initialize ...
- (转)简单的RPC java实现 .
转:http://blog.csdn.net/jackliang55/article/details/7580563 我也承认,RPC的名声大噪之时是在2003年,那一个“冲击波”病毒(Blaster ...
- 牛客多校第十场 D Han Xin and His Troops 中国剩余定理
题意: 韩信有若干个兵,给定你若干个模数和余数,再给你一个1e18以内的范围限制,求解同余方程组,如果无解,输出“他一定在撒谎”,如果最小解超出范围限制,输出“他可能在撒谎”,否则输出最小解 注意:不 ...
- postman中如何传数组
方法一: postman的传参: java接收: package com.nps.base.xue.xd.groovyEngine import com.google.gson.Gson import ...
- 牛客练习赛26 A 平面(结论题)
题目链接:https://ac.nowcoder.com/acm/contest/907/A 思路:结论题,当做2n条直线,参考资料https://blog.csdn.net/qq_34131212/ ...
- 【模板篇】A* 寻路算法
上次在做k短路的时候说到了A*, 但是并没有仔细的研究A*寻路, 毕竟k短路中的A*也不怎么标准… A*寻路的过程网上还是有很多的, 讲得也很清楚, 不妨跟着里面的图示自己动手操作一下, 基本一遍就能 ...