Codeforces 434E - Furukawa Nagisa's Tree(三元环+点分治)
场号 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\) 的贡献即可写作:
\]
接下来考虑怎样求出 \(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(三元环+点分治)的更多相关文章
- 【CF434E】Furukawa Nagisa's Tree 点分治
[CF434E]Furukawa Nagisa's Tree 题意:一棵n个点的树,点有点权.定义$G(a,b)$表示:我们将树上从a走到b经过的点都拿出来,设这些点的点权分别为$z_0,z_1... ...
- Codeforces Gym 100342J Problem J. Triatrip 三元环
题目链接: http://codeforces.com/gym/100342 题意: 求三元环的个数 题解: 用bitset分别统计每个点的出度的边和入度的边. 枚举每一条边(a,b),计算以b为出度 ...
- Codeforces Gym 100342J Problem J. Triatrip 求三元环的数量 bitset
Problem J. Triatrip Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100342/at ...
- Codeforces Gym 100342J Problem J. Triatrip bitset 求三元环的数量
Problem J. TriatripTime Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100342/att ...
- Codeforces 985G - Team Players(三元环)
Codeforces 题目传送门 & 洛谷题目传送门 真·ycx 做啥题我就做啥题 考虑枚举 \(j\),我们预处理出 \(c1_i\) 表示与 \(i\) 相连的编号 \(<i\) 的 ...
- BZOJ.5407.girls(容斥 三元环)
题目链接 CF 原题 \(Description\) 有n个点,其中有m条边连接两个点.每一个没有连边的三元组\((i,j,k)(i<j<k)\)对答案的贡献为\(A*i+B*j+C*k\ ...
- BZOJ 3498 PA2009 Cakes(三元环处理)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3498 [题目大意] N个点m条边,每个点有一个点权a. 对于任意一个三元环(j,j,k ...
- HDU 6184 Counting Stars 经典三元环计数
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6184 题意: n个点m条边的无向图,问有多少个A-structure 其中A-structure满足V ...
- BZOJ3498PA2009 Cakes——三元环
题目描述 N个点m条边,每个点有一个点权a.对于任意一个三元环(j,j,k)(i<j<k),它的贡献为max(ai,aj,ak) 求所有三元环的贡献和.N<100000,,m< ...
随机推荐
- 【UE4 C++】绘制函数 Debug drawing functions
基于UKismetSystemLibrary /** Draw a debug line */ UFUNCTION(BlueprintCallable, Category="Renderin ...
- 关于takin-data,你想知道的都在这里(二)trace日志篇
相信大家在使用takin的过程中都见到过压测过程中实时展示的请求流量明细和请求详情了吧,像这样: 还有这样: 这样的请求流量明细和调用链详情是怎么实现的呢,今天就带大家探究下. 在前面的启动命令篇(h ...
- AIApe问答机器人Scrum Meeting 4.27
Scrum Meeting 3 日期:2021年4月27日 会议主要内容概述:汇报两日工作. 一.进度情况 组员 负责 两日内已完成的工作 后两日计划完成的工作 工作中遇到的困难 李明昕 后端 Tas ...
- 无判断max 牛客网 程序员面试金典 C++ Python
无判断max 牛客网 程序员面试金典 C++ Python 题目描述 请编写一个方法,找出两个数字中最大的那个.条件是不得使用if-else等比较和判断运算符. 给定两个int a和b,请返回较大的 ...
- Linux 显示ip、dns、网关等命令
在新版的ubuntu 终端里输入命令nm-tool, 想查看网络参数设置, 没想到却返回如下内容: 未找到 'nm-tool' 命令,您要输入的是否是: 命令 'dm-tool' 来自于包 'l ...
- LeetCode88 合并有序数组
1. 这道题为简单题目,但是还有需要好好思考的 2. 首先不能使用额外数组合并,不然就没得后文了 3. nums1后面有0填充,且填充数量正好是n,整个数组大小即m+n能够容纳合并后的数据 4.既然要 ...
- elasitcsearch单机版安装
1.下载压缩包 https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.6.2.tar.gz 2.解压修改配置文件 c ...
- Linux mem 2.7 内存错误检测 (KASAN) 详解
文章目录 1. 简介 2. Shadow 区域初始化 3. 权限的判断 3.1 read/write 3.2 memxxx() 4. 权限的设置 4.1 buddy 4.1.1 kasan_free_ ...
- PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilder,阿里MAVEN仓库地址更新为了https问题
http://maven.aliyun.com/nexus/content/groups/public/,仓库地址更新为了https,所以下载时需要ssl认证,我们可以忽略ssl检查导致的问题,我们可 ...
- FZU ICPC 2020 寒假训练 2
A - 排序 输入一行数字,如果我们把这行数字中的'5'都看成空格,那么就得到一行用空格分割的若 干非负整数(可能有些整数以'0'开头,这些头部的'0'应该被忽略掉,除非这个整数就是由 若干个'0'组 ...