标签:work FN 枚举 algorithm etc 节点 const Nid getch
给一棵树,每个节点有一个权值$val$。
如果两个点$a$和$b$满足$a$为$b$的祖先且$val[b]$为$val[a]$的约数,那么可以从$a$一步跳到$b$。
求从$1$号节点走到各每个节点的路径数。
$n \leq 10^5 , val[i] \leq 10^{18},$保证对于任意节点$i$,$val[i]$为$val[1]$的约数。
首先有定理:一个数$n$的约数个数大概是$n^{\frac{1}{3}}$的级别
然后得到有理有据的一个部分分($val[i] \leq 10^9,40$分)算法:
对$val[1]$分解质因数,求出其所有约数,然后开等同于约数个数个桶,每到一个节点就枚举每个约数看是否为自己的倍数,如果是就取出对应桶中的值加入当前点的$dp$值,最后再把当前点也加入桶中。
(事实上对所有$val$进行一次$unique$就可以不用分解质因数了)
复杂度$O(n*10^6)$,显然不能过。
于是来一发神奇的分块高维前缀和。
具体而言,将每个数分解为若干个质因数的乘积,那么就可以将这个数看成一个向量,每一维的值对应其某一个质因数的数量。
将向量从中间划开,分成尽量相等的两组维度,然后对于其中一组维度做前缀和。
也就是说在此题中,假如是一个二维向量$(i,j)$,对应质因数$2^i*3^j$。
此时,将向量划成$i$和$j$两组维度,那么将向量$i,j$的贡献$v$加入的过程如下:
for(int k=i;k<=max_i;k++)
sum[k][j]+=v
查询向量$(i,j)$的前缀和则用:
for(int k=j;k<=max_j;k++)
ans+=sum[i][k]
此时查询复杂度和修改复杂度均为$O(sqrt(n))$,$n$为所有维度的$max_i$之积,在这里为$10^6$。
于是复杂度降为$O(n*10^3)=O(10^8)$,玄学卡常一波即可通过~
由于要分解$10^{18}$级别的大数,还需要来一发Pollard-Rho......
代码:
#include<map>
#include<vector>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1e5+9;
const ll md=1e9+7;
inline ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0' || '9'<ch){if(ch=='-')f=-1;ch=getchar();}
while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
return x*f;
}
inline void write(ll x)
{
if(x>=10)write(x/10);
putchar('0'+x%10);
}
int n;
int to[N<<1],nxt[N<<1],beg[N],tot;
int fa[N],id[N],ed[N],seg[N],dfn;
ll val[N],f[N];
inline void add(int u,int v)
{
to[++tot]=v;
nxt[tot]=beg[u];
beg[u]=tot;
}
namespace di
{
ll stk[100],cnt[100],top,c;
vector<ll> vec,vecs;
vector<ll> vf;
map<ll,int> ha;
inline ll gcd(ll a,ll b){return !b?a:gcd(b,a%b);}
inline ll mul(ll a,ll b,ll p)
{
return ((a*b-(ll)((long double)a*b/p)*p)%p+p)%p;
}
inline ll qpow(ll a,ll b,ll p)
{
ll ret=1;
while(b)
{
if(b&1)ret=mul(ret,a,p);
a=mul(a,a,p);b>>=1;
}
return ret;
}
inline bool check(ll a,ll n)
{
ll x=n-1,t=0;
for(;!(x&1);x>>=1)t++;
x=qpow(a,x,n);
if(x==1 || x==n-1)return 1;
x=mul(x,x,n);
for(int i=1;i<=t;i++,x=mul(x,x,n))
{
if(x==n-1)return 1;
if(x==1)return 0;
}
return 0;
}
inline bool miller_rabin(ll x)
{
if(x==1 || x==2)return 1;
for(int i=1;i<=15;i++)
if(!check(rand()%(x-1)+1,x))
return 0;
return 1;
}
inline void find(ll x);
inline bool pollard_rho(ll x)
{
int cnt=0,k=0;
ll x1=rand()%x,x2=x1,d;
while(1)
{
x1=(mul(x1,x1,x)+c)%x;
d=gcd(abs(x1-x2),x);
if(d!=1 && d!=x)
return find(x/d),find(d),1;
if(x1==x2)return 0;
if((++cnt)==(1<<k))
k++,x2=x1;
}
}
inline void find(ll x)
{
if(miller_rabin(x))
{
stk[++top]=x;
cnt[top]=0;
return;
}
while(!pollard_rho(x))
c=rand();
}
inline void work(ll x)
{
top=0;find(x);int e=top;
sort(stk+1,stk+top+1);
top=0;stk[0]=-1;
for(int i=1;i<=e;i++)
{
if(stk[i]!=stk[top])
{
stk[++top]=stk[i];
cnt[top]=0;
}
cnt[top]++;
}
}
inline void dfss(int x,ll v)
{
if(x==top+1)
{
vec.push_back(v);
vf.push_back(0);
ha[v]=vec.size()-1;
return;
}
ll mul=1;
for(int i=0;i<=cnt[x];i++,mul*=stk[x])
dfss(x+1,v*mul);
}
}
using namespace di;
inline void chk(ll &x){if(x>=md)x-=md;}
namespace sbds
{
const int M=5009;
int mid;
ll ds[M][M];
inline void init(ll x)
{
work(x);
mid=top>>1;
}
inline void modifys(ll x,int nid,int id,ll v,int p)
{
if(p==mid+1)
{
chk(ds[nid][id]+=v);
return;
}
int cnts=0;
while(x/stk[p]*stk[p]==x)
x/=stk[p],cnts++;
for(int i=cnts;i>=0;i--)
modifys(x,nid*(cnt[p]+1)+i,id,v,p+1);
}
inline void modify(ll x,ll v)
{
int id=0;
for(int i=mid+1;i<=top;i++)
{
int cnts=0;
while(x/stk[i]*stk[i]==x)
x/=stk[i],cnts++;
id=id*(cnt[i]+1)+cnts;
}
modifys(x,0,id,v,1);
}
inline ll querys(ll x,int nid,int id,int p)
{
if(p==top+1)
return ds[id][nid];
int cnts=0;ll ret=0;
while(x/stk[p]*stk[p]==x)
x/=stk[p],cnts++;
for(int i=cnts;i<=cnt[p];i++)
chk(ret+=querys(x,nid*(cnt[p]+1)+i,id,p+1));
return ret;
}
inline ll query(ll x)
{
int id=0;
for(int i=1;i<=mid;i++)
{
int cnts=0;
while(x/stk[i]*stk[i]==x)
x/=stk[i],cnts++;
id=id*(cnt[i]+1)+cnts;
}
return querys(x,0,id,mid+1);
}
}
inline void dfs(int u)
{
if(u!=1)
f[u]=sbds::query(val[u]);
sbds::modify(val[u],f[u]);
for(int i=beg[u];i;i=nxt[i])
if(to[i]!=fa[u])
fa[to[i]]=u,dfs(to[i]);
sbds::modify(val[u],md-f[u]);
}
int main()
{
srand(514);n=read();
for(int i=2,u,v;i<=n;i++)
{
u=read();v=read();
add(u,v);add(v,u);
}
for(int i=1;i<=n;i++)
val[i]=read();
sbds::init(val[1]);
dfs(f[1]=1);
for(int i=1;i<=n;i++)
write(f[i]),putchar('\n');
return 0;
}
[2018.6.21集训]走路-分块高维前缀和-Pollard-Rho
标签:work FN 枚举 algorithm etc 节点 const Nid getch
原文地址:https://www.cnblogs.com/zltttt/p/9211475.html