Codeforces 题面传送门 & 洛谷题面传送门

场号 hopping,刚好是我的学号(指 round 的编号)

注:下文中分别用 \(X,Y,K\) 代替题目中的 \(x,y,k\)

注意到这东西长得有点像三元环,因此考虑往三元环方面考虑。我们重新建立一张图,对于两点 \(x,y\),如果 \(x\to y\) 路径对应的权值为 \(X\)(即 \(x\to y\) 这条路径属于 Furukawa Nagisa),那么我们就在 \(x\to y\) 之间连一条权值为 \(1\) 的边,否则我们在 \(x\to y\) 之间连一条权值为 \(0\) 的边,那么我们要求的即为有多少个三元组 \((x,y,z)\),满足 \(x\to y,y\to z,x\to z\) 之间全是权值为 \(1\) 的边,或者全是权值为 \(0\) 的边。我们考虑借鉴竞赛图三元环计数的一个小 trick:从反面考虑。具体来说我们考虑不合法的情况长什么样,不难发现,对于每种不合法的情况都恰好有两个点,满足与它相连的两条边恰好一条权值为 \(0\),一条权值为 \(1\)。我们记这样的点为这个三元组的“代表点”。我们考虑对每个点分别统计有多少个三元组,满足其在该三元组中作为一个代表点出现,那么一个不合法的三元组会恰好被计算两次,因此将这个数目除以 \(2\) 就是不合法的三元组个数。

那么怎么计算每个点在多少个三元组中作为代表点呢?注意到对于一个点 \(x\) 而言,与其相连的点可以被分为四类:\(y\to x\),权值为 \(0/1\),\(x\to y\),权值为 \(0/1\)。因此考虑将与 \(x\) 相连的两条边,一条权值为 \(0\),一条权值为 \(1\) 的情况分为以下四类:

  • \(y\to x\)​,权值为 \(0\)​,\(z\to x\)​,权值为 \(1\)​
  • \(y\to x\),权值为 \(0\),\(x\to z\),权值为 \(1\)
  • \(x\to y\),权值为 \(0\),\(z\to x\),权值为 \(1\)
  • \(x\to y\),权值为 \(0\),\(x\to z\),权值为 \(1\)

不难发现对于第一种和第四种情况,\(y,z\) 之间的边既可以是 \(y\to z\),也可以是 \(z\to x\),因此贡献应该乘 \(2\),而对于第二种和第三种情况,\(y,z\) 之间边的指向唯一。故如果我们已经求出 \(c1_x\) 表示有多少个 \(y\) 满足 \(y\to x\) 的边权值为 \(1\),\(c2_x\) 表示有多少个 \(y\) 满足 \(x\to y\) 的边的权值为 \(1\),那么一个点 \(x\) 的贡献即可写作:

\[2c1_x·(n-c1_x)+2c2_x·(n-c2_x)+c1_x·(n-c2_x)+c2_x·(n-c1_x)
\]

接下来考虑怎样求出 \(c1_x,c2_x\)​,注意到这里涉及树上路径统计,因此考虑点分治,于是问题可以转化为如何求出经过当前分治中心 \(x\)​ 的路径的贡献。对于 \(x\)​ 所在的连通块中的点 \(y\)​,我们考虑一遍 DFS 找出 \(x\to y\)​ 路径上所有点 \(x=p_1,p_2,p_3,\cdots,p_k=y\)​,那么我们考虑设 \(s_y=\sum\limits_{i=2}^ka_{p_i}·K^{k-i},t_y=\sum\limits_{i=1}^ka_{p_i}·K^{i-1}\)​,那么对于一条 \(u\to v\)​ 的路径,其权值可以写作 \(s_u+t_v·K^{dep_u}\)​。我们考虑从左到右遍历所有子树,那么 \(c1_v\)​ 会加上满足 \(s_u+t_v·K^{dep_u}\equiv X\pmod{Y}\)​ 的 \(u\)​ 的个数,这个可以开一个桶 \(b1\)​,然后每加入一个点 \(u\)​ 时,就在 \((X-s_u)·K^{-dep_u}\)​ 位置加 \(1\)​,然后通过调用 \(b1_{t_v}\)​ 即可求出符合要求的 \(u\) 的个数。\(c2_v\) 的贡献也同理,一个点 \(u\) 会对 \(c2_v\) 产生贡献当且仅当 \(s_v+t_u·K^{dep_v}\equiv X\pmod{Y}\),化简可得 \(t_u\equiv(X-s_v)·K^{-dep_u}\pmod{Y}\),同样开个桶 \(b2\) 维护一下即可。

时间复杂度 \(n\log^2n\)

const int MAXN=1e5;
const int INF=0x3f3f3f3f;
int n,X,Y,k,a[MAXN+5];
int hd[MAXN+5],to[MAXN*2+5],nxt[MAXN*2+5],ec=0;
int qpow(int x,int e){
int ret=1;
for(;e;e>>=1,x=1ll*x*x%Y) if(e&1) ret=1ll*ret*x%Y;
return ret;
}
int pw[MAXN+5],ipw[MAXN+5];
void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
int siz[MAXN+5],cent=0,mx[MAXN+5];bool vis[MAXN+5];
void findcent(int x,int f,int tot){
siz[x]=1;mx[x]=0;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];if(y==f||vis[y]) continue;
findcent(y,x,tot);siz[x]+=siz[y];
chkmax(mx[x],siz[y]);
} chkmax(mx[x],tot-siz[x]);
if(mx[x]<mx[cent]) cent=x;
}
int dep[MAXN+5],f1[MAXN+5],f2[MAXN+5];
void getdep(int x,int f){
// printf("%d %d %d %d\n",x,dep[x],f1[x],f2[x]);
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];if(y==f||vis[y]) continue;
dep[y]=dep[x]+1;f1[y]=(1ll*f1[x]*k+a[y])%Y;
f2[y]=(f2[x]+1ll*a[y]*pw[dep[y]])%Y;
getdep(y,x);
}
}
vector<int> pt;
int res1[MAXN+5],res2[MAXN+5];
void getpts(int x,int f){
pt.pb(x);
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];if(y==f||vis[y]) continue;
getpts(y,x);
}
}
void divcent(int x){
// printf("divcent %d\n",x);
vis[x]=1;f1[x]=0;f2[x]=a[x];dep[x]=0;
map<int,int> cnt1,cnt2;
#define insert1(x) cnt1[1ll*(X-f1[x]+Y)*ipw[dep[x]]%Y]++
#define insert2(x) cnt2[f2[x]]++
#define calc(x) (res1[x]+=cnt1[f2[x]],res2[x]+=cnt2[1ll*(X-f1[x]+Y)*ipw[dep[x]]%Y])
insert1(x);insert2(x);stack<int> stk;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];if(vis[y]) continue;
f1[y]=a[y];f2[y]=(1ll*a[y]*k+a[x])%Y;
dep[y]=1;getdep(y,x);
}
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];if(vis[y]) continue;
// printf("y=%d\n",y);
pt.clear();getpts(y,x);stk.push(y);
for(int z:pt) calc(z);
for(int z:pt) insert1(z),insert2(z);
} cnt1.clear();cnt2.clear();
while(!stk.empty()){
int y=stk.top();stk.pop();
// printf("y=%d\n",y);
pt.clear();getpts(y,x);
for(int z:pt) calc(z);
for(int z:pt) insert1(z),insert2(z);
} calc(x);
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];if(vis[y]) continue;
cent=0;findcent(y,x,siz[y]);divcent(cent);
}
}
int main(){
scanf("%d%d%d%d",&n,&Y,&k,&X);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),adde(u,v),adde(v,u);
pw[0]=ipw[0]=1;pw[1]=k;ipw[1]=qpow(k,Y-2);
for(int i=2;i<=n;i++){
pw[i]=1ll*pw[i-1]*pw[1]%Y;
ipw[i]=1ll*ipw[i-1]*ipw[1]%Y;
} mx[0]=INF;findcent(1,0,n);divcent(cent);
for(int i=1;i<=n;i++) if(a[i]==X) res1[i]++,res2[i]++;
// for(int i=1;i<=n;i++) printf("%d %d\n",res1[i],res2[i]);
ll res=0;
for(int i=1;i<=n;i++){
int rst1=n-res1[i],rst2=n-res2[i];
res+=2ll*rst1*res1[i];res+=1ll*rst1*res2[i];
res+=1ll*rst2*res1[i];res+=2ll*rst2*res2[i];
} printf("%lld\n",1ll*n*n*n-(res>>1));
return 0;
}

Codeforces 434E - Furukawa Nagisa's Tree(三元环+点分治)的更多相关文章

  1. 【CF434E】Furukawa Nagisa's Tree 点分治

    [CF434E]Furukawa Nagisa's Tree 题意:一棵n个点的树,点有点权.定义$G(a,b)$表示:我们将树上从a走到b经过的点都拿出来,设这些点的点权分别为$z_0,z_1... ...

  2. Codeforces Gym 100342J Problem J. Triatrip 三元环

    题目链接: http://codeforces.com/gym/100342 题意: 求三元环的个数 题解: 用bitset分别统计每个点的出度的边和入度的边. 枚举每一条边(a,b),计算以b为出度 ...

  3. Codeforces Gym 100342J Problem J. Triatrip 求三元环的数量 bitset

    Problem J. Triatrip Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100342/at ...

  4. Codeforces Gym 100342J Problem J. Triatrip bitset 求三元环的数量

    Problem J. TriatripTime Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100342/att ...

  5. Codeforces 985G - Team Players(三元环)

    Codeforces 题目传送门 & 洛谷题目传送门 真·ycx 做啥题我就做啥题 考虑枚举 \(j\),我们预处理出 \(c1_i\) 表示与 \(i\) 相连的编号 \(<i\) 的 ...

  6. BZOJ.5407.girls(容斥 三元环)

    题目链接 CF 原题 \(Description\) 有n个点,其中有m条边连接两个点.每一个没有连边的三元组\((i,j,k)(i<j<k)\)对答案的贡献为\(A*i+B*j+C*k\ ...

  7. BZOJ 3498 PA2009 Cakes(三元环处理)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3498 [题目大意] N个点m条边,每个点有一个点权a. 对于任意一个三元环(j,j,k ...

  8. HDU 6184 Counting Stars 经典三元环计数

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6184 题意: n个点m条边的无向图,问有多少个A-structure 其中A-structure满足V ...

  9. BZOJ3498PA2009 Cakes——三元环

    题目描述 N个点m条边,每个点有一个点权a.对于任意一个三元环(j,j,k)(i<j<k),它的贡献为max(ai,aj,ak) 求所有三元环的贡献和.N<100000,,m< ...

随机推荐

  1. 痞子衡嵌入式:超级下载算法RT-UFL v1.0在IAR EW for Arm下的使用

    痞子衡主导的"学术"项目 <RT-UFL - 一个适用全平台i.MXRT的超级下载算法设计> v1.0 版发布近 4 个月了,部分客户已经在实际项目开发调试中用上了这个 ...

  2. Python设置Excel样式

    前面已经详细讲解过使用Python对Excel表格进行读.写操作,本文主要讲解下使用Python设置Excel表格的样式. 深入学习请参考openpyxl官方文档: https://openpyxl. ...

  3. python web1

    ***本篇中的测试均需要使用python3完成. 攻击以下面脚本运作的服务器. 针对脚本的代码逻辑,写出生成利用任意代码执行漏洞的恶意序列的脚本: 打开攻击机端口, 将生成的东西输入网页cookie: ...

  4. 手把手教你学Dapr - 1. .Net开发者的大时代

    Dapr全称 Distributed Application Runtime,分布式应用运行时 Dapr的口号 简化云原生应用开发,聚焦在应用的核心逻辑,让代码简单.可移植 Dapr的目标 最佳实践的 ...

  5. 第5次 Beta Scrum Meeting

    本次会议为Beta阶段第6次Scrum Meeting会议 会议概要 会议时间:2021年6月6日 会议地点:「腾讯会议」线上进行 会议时长:10min 会议内容简介:对完成工作进行阶段性汇报:对下一 ...

  6. 网络摄像机中的IR-CUT详解

    自然界存在着各种波长的光线,通过折射人眼能看到不同颜色的光线,这就是光线的波长不同所导致的.其实还有许多光线是人眼看不到的,人眼识别光线的波长范围在320nm-760nm之间,超过760nm的光线人眼 ...

  7. 用C++实现的数独解题程序 SudokuSolver 2.6 的新功能及相关分析

    SudokuSolver 2.6 的新功能及相关分析 SudokuSolver 2.6 的命令清单如下: H:\Read\num\Release>sudoku.exe Order please: ...

  8. 助你上手Vue3全家桶之VueX4教程

    目录 1,前言 2,State 2.1,直接使用 2.2,结合computed 3,Getter 3.1,直接使用 3.2,结合computed 4,Mutation 4.1,直接使用 4.2,结合c ...

  9. simulate_screencap

    #!/bin/bashadb shell screencap -p /sdcard/screen.pngadb pull /sdcard/screen.png ./adb shell rm /sdca ...

  10. 安装配置多个版本JDK

    前言:JDK有多个版本,有时为了开发需要切换不同的版本,在一部电脑上安装多个JDK,只需要按以下配置,每次即可轻松使用.以下环境为Windows10 安装JDK 安装JDK8 配置环境变量 需要配置J ...