【点分治】【FFT】CDOJ1562 Amaz1ng Prime
统计路径的时候,显然用母函数的思想,可以用FFT来方便统计。
注意!要减去路径两个端点相同的情况!然后再除以二!这样防止重复。
还有就是说啊,点分治的正确姿势还是应该用所有子树的答案减去各个子树分别的答案。否则复杂度好像是不太对哈。
除非……有一种情况是这样的……才能让每次直接统计当前子树和之前所有子树的答案而不重复统计再减去的写法复杂度正确。
即:用个set或者哈希表之类的东西存储之前所有子树的路径,然后我只枚举当前子树的路径,往set或者哈希表里面去查询。查询完了以后,再把当前子树塞到set/哈希表里。
这样复杂度正确的原因是,set和哈希表不会因为其储存元素的多少而使复杂度快速上升。
FFT貌似是得开4倍数组哈。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
using namespace std;
typedef long long ll;
#define MAXN 100010
bool notPrime[200010];
void Shai()
{
notPrime[1]=1;
notPrime[0]=1;
for(ll i=2;i<=200000ll;i++)
for(ll j=i*i;j<=200000ll;j+=i)
notPrime[j]=1;
}
const double PI = acos(-1.0);
struct Complex{
double real,image;
Complex(double _real,double _image){
real=_real;
image=_image;
}
Complex(){}
};
Complex operator + (const Complex &c1,const Complex &c2){
return Complex(c1.real+c2.real,c1.image+c2.image);
}
Complex operator - (const Complex &c1,const Complex &c2){
return Complex(c1.real-c2.real,c1.image-c2.image);
}
Complex operator * (const Complex &c1,const Complex &c2){
return Complex(c1.real*c2.real-c1.image*c2.image,c1.real*c2.image+c1.image*c2.real);
}
int rev(int id,int len){
int ret=0;
for(int i=0;(1<<i)<len;++i){
ret<<=1;
if(id&(1<<i)){
ret|=1;
}
}
return ret;
}
Complex tmp[200010];
//当DFT==1时是DFT, DFT==-1时则是逆DFT
void IterativeFFT(Complex A[],int len, int DFT){//对长度为len(2的幂)的数组进行DFT变换
for(int i=0;i<len;++i){
tmp[rev(i,len)]=A[i];
}
for(int i=0;i<len;++i){
A[i]=tmp[i];
}
for(int s=1;(1<<s)<=len;++s){
int m=(1<<s);
Complex wm=Complex(cos(DFT*2*PI/m),sin(DFT*2*PI/m));
for(int k=0;k<len;k+=m){//这一层结点包含的数组元素个数都是(1<<s)
Complex w=Complex(1,0);
for(int j=0;j<(m>>1);++j){//折半引理,根据两个子节点计算父节点
Complex t=w*A[k+j+(m>>1)];
Complex u=A[k+j];
A[k+j]=u+t;
A[k+j+(m>>1)]=u-t;
w=w*wm;
}
}
}
if(DFT==-1){
for(int i=0;i<len;++i){
A[i].real/=len;
A[i].image/=len;
}
}
}
typedef pair<int,int> Point;
int n;
ll ans;
int v[MAXN<<1],w[MAXN<<1],first[MAXN],__next[MAXN<<1],en;
void AddEdge(const int &U,const int &V,const int &W)
{
v[++en]=V;
w[en]=W;
__next[en]=first[U];
first[U]=en;
}
bool centroid[MAXN];//顶点是否已经作为重心删除的标记
int size[MAXN];//以该顶点为根的子树的大小
//计算子树的大小
int calc_sizes(int U,int Fa)
{
int res=1;
for(int i=first[U];i;i=__next[i])
if(v[i]!=Fa&&(!centroid[v[i]]))
res+=calc_sizes(v[i],U);
return size[U]=res;
}
//查找重心的递归函数,nn是整个子树的大小
//在以U为根的子树中寻找一个顶点,使得删除该顶点后得到的最大子树的顶点数最少
//返回值为(最大子树的顶点数,顶点编号)
Point calc_centroid(int U,int Fa,int nn)
{
Point res=make_pair(2147483647,-1);
int sum=1,maxv=0;
for(int i=first[U];i;i=__next[i])
if(v[i]!=Fa&&(!centroid[v[i]]))
{
res=min(res,calc_centroid(v[i],U,nn));
maxv=max(maxv,size[v[i]]);
sum+=size[v[i]];
}
maxv=max(maxv,nn-sum);
res=min(res,make_pair(maxv,U));
return res;
}
int td[MAXN],en2,ds[MAXN],en3;
//计算子树中所有顶点到重心的距离的递归函数
void calc_dis(int U,int Fa,int d)
{
td[en2++]=d;
for(int i=first[U];i;i=__next[i])
if(v[i]!=Fa&&(!centroid[v[i]]))
calc_dis(v[i],U,d+w[i]);
}
Complex fft[200100];
ll calc_pairs(int dis[],int En)
{
int lim=0;
for(int i=0;i<En;++i){
lim=max(lim,dis[i]);
}
++lim;
int len;
for(int i=0;;++i){
if((1<<(i-1))>=lim){
len=(1<<i);
break;
}
}
for(int i=0;i<len;++i){
fft[i]=Complex(0,0);
}
for(int i=0;i<En;++i){
fft[dis[i]].real+=1.0;
}
IterativeFFT(fft,len,1);
for(int i=0;i<len;++i){
fft[i]=fft[i]*fft[i];
}
IterativeFFT(fft,len,-1);
ll res=0;
for(int i=0;i<len;++i){
if(!notPrime[i]){
res+=(ll)(fft[i].real+0.5);
}
}
for(int i=0;i<En;++i){
if(!notPrime[dis[i]<<1]){
--res;
}
}
return (res>>1);
}
void solve(int U)
{
calc_sizes(U,-1);
int s=calc_centroid(U,-1,size[U]).second;
centroid[s]=1;
//情况1:递归统计按重心s分割后的子树中的对数
for(int i=first[s];i;i=__next[i])
if(!centroid[v[i]])
solve(v[i]);
//情况2:统计经过重心s的对数
en3=0; ds[en3++]=0;
for(int i=first[s];i;i=__next[i])
if(!centroid[v[i]])
{
en2=0; calc_dis(v[i],s,w[i]);
ans-=calc_pairs(td,en2);//先把重复统计的部分(即情况1)减掉
memcpy(ds+en3,td,en2*sizeof(int)); en3+=en2;
}
ans+=calc_pairs(ds,en3);
centroid[s]=0;
}
int main()
{
// freopen("cdoj1562.in","r",stdin);
Shai();
int a,b,c;
scanf("%d",&n);
for(int i=1;i<n;++i)
{
scanf("%d%d%d",&a,&b,&c);
AddEdge(a,b,c);
AddEdge(b,a,c);
}
solve(1);
cout<<ans<<endl;
return 0;
}
【点分治】【FFT】CDOJ1562 Amaz1ng Prime的更多相关文章
- prime distance on a tree(点分治+fft)
最裸的点分治+fft,调了好久,太菜了.... #include<iostream> #include<cstring> #include<cstdio> #inc ...
- bzoj 3456 城市规划——分治FFT / 多项式求逆 / 多项式求ln
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3456 分治FFT: 设 dp[ i ] 表示 i 个点时连通的方案数. 考虑算补集:连通的方 ...
- BNUOJ 51279[组队活动 Large](cdq分治+FFT)
传送门 大意:ACM校队一共有n名队员,从1到n标号,现在n名队员要组成若干支队伍,每支队伍至多有m名队员,求一共有多少种不同的组队方案.两个组队方案被视为不同的,当且仅当存在至少一名队员在两种方案中 ...
- hdu 5730 Shell Necklace [分治fft | 多项式求逆]
hdu 5730 Shell Necklace 题意:求递推式\(f_n = \sum_{i=1}^n a_i f_{n-i}\),模313 多么优秀的模板题 可以用分治fft,也可以多项式求逆 分治 ...
- BZOJ 4555: [Tjoi2016&Heoi2016]求和 [分治FFT 组合计数 | 多项式求逆]
4555: [Tjoi2016&Heoi2016]求和 题意:求\[ \sum_{i=0}^n \sum_{j=0}^i S(i,j)\cdot 2^j\cdot j! \\ S是第二类斯特林 ...
- 分治FFT的三种含义
分治FFT是几个算法的统称.它们之间并无关联. 分治多项式乘法 问题如求\(\prod_{i=1}^na_ix+b\). 若挨个乘复杂度为\(O(n^2\log n)\),可分治做这件事,复杂度为\( ...
- 【XSY2666】排列问题 DP 容斥原理 分治FFT
题目大意 有\(n\)种颜色的球,第\(i\)种有\(a_i\)个.设\(m=\sum a_i\).你要把这\(m\)个小球排成一排.有\(q\)个询问,每次给你一个\(x\),问你有多少种方案使得相 ...
- 【XSY2887】【GDOI2018】小学生图论题 分治FFT 多项式exp
题目描述 在一个 \(n\) 个点的有向图中,编号从 \(1\) 到 \(n\),任意两个点之间都有且仅有一条有向边.现在已知一些单向的简单路径(路径上任意两点各不相同),例如 \(2\to 4\to ...
- 【XSY2744】信仰圣光 分治FFT 多项式exp 容斥原理
题目描述 有一个\(n\)个元素的置换,你要选择\(k\)个元素,问有多少种方案满足:对于每个轮换,你都选择了其中的一个元素. 对\(998244353\)取模. \(k\leq n\leq 1525 ...
随机推荐
- js中的document.ready
1.概念 表示在dom结构绘制完成后执行,可能DOM元素关联的部分并未加载完 2.写法 $(document).on("ready",function(){ }) $(docume ...
- Ubuntu中启用关闭Network-manager网络设置问题! 【Server版本】
在UbuntuServer版本中,因为只存有命令行模式,所以要想进行网络参数设置,只能通过修改/etc/network/interfaces.具体设置方法如下: (1) UbuntuServer 修改 ...
- 配置连接的IP、端口、以及相应的数据库
解压后里面有:lib 源文件 .examples 例子.test测试 将lib目录拷贝到你的项目中,就可以开始你的predis操作了. //使用autoload加载相关库,这边重点就是为了requir ...
- MediaWiki安装配置(Linux)【转】
转自:http://blog.csdn.net/gao36951/article/details/43965527 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] 1Media ...
- Python爬虫音频数据
一:前言 本次爬取的是喜马拉雅的热门栏目下全部电台的每个频道的信息和频道中的每个音频数据的各种信息,然后把爬取的数据保存到mongodb以备后续使用.这次数据量在70万左右.音频数据包括音频下载地址, ...
- 2.ubuntu的使用
1. CTRL+ALT+T 可以将命令模式打开 2. 有可能没办法进行yum ,它会告诉你操作的方法 3. 有些操作需要获得root的权限才可以,我们得进入root状态 --> sudo pas ...
- Struts2学习笔记02 之 使用
一.页面向Action传参 1.基本属性注入,页面命名name,action提供成员变量name并提供set方法. 2.域模型注入:页面用user.name对象点属性形式.action成员user对象 ...
- 【python】资料记录
今天看了一些关于python的知识: 1.装饰器:https://www.zhihu.com/question/25950466/answer/31731502 2.*args的用法:http://b ...
- Java中的原子操作类
转载: <ava并发编程的艺术>第7章 当程序更新一个变量时,如果多线程同时更新这个变量,可能得到期望之外的值,比如变量i=1,A线程更新i+1,B线程也更新i+1,经过两个线程操作之后可 ...
- 不同版本的jquery的复选框checkbox的相关问题
在尝试写复选框时候遇到一个问题,调试了很久都没调试出来,极其郁闷: IE10,Chrome,FF中,对于选中状态,第一次$('#checkbox').attr('checked',true)可以实现 ...