[2018.6.21集训]走路-分块高维前缀和-Pollard-Rho

题目大意

给一棵树,每个节点有一个权值$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;
}

原文地址:https://www.cnblogs.com/zltttt/p/9211475.html

时间: 2024-08-27 14:05:15

[2018.6.21集训]走路-分块高维前缀和-Pollard-Rho的相关文章

高维前缀和

我们经常要用到前缀和. 一维: for(int i=1;i<=n;i++) b[i]=b[i-1]+a[i]; 二维: for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) b[i][j]=b[i-1][j]+b[i][j-1]-b[i-1][j-1]+a[i][j]; 那如果是三维的呢? for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) for(int k=1;k<=p;k++) b[i][j][

codeforces 938F(dp+高维前缀和)

题意: 给一个长度为n的字符串,定义$k=\floor{log_2 n}$ 一共k轮操作,第i次操作要删除当前字符串恰好长度为$2^{i-1}$的子串 问最后剩余的字符串字典序最小是多少? 分析: 首先很容易得到一个性质,那就是删除的那些串是可以不交叉的 很容易想到一个很简单的dp dp[i][j]表示考虑原串的前i位,删除状态为j的情况下字典序最小的字符串(注意dp里面保存的是个字符串) 那么很明显就是个O(n^3logn)的dp,无法通过 dp里是一个字符串这个东西是很浪费时间而且很不优美的

4.7-4.9补题+水题+高维前缀和

题目链接:51nod 1718 Cos的多项式  [数学] 题解: 2cosx=2cosx 2cos2x=(2cosx)^2-2 2cos3x=(2cosx)^3-3*(2cosx) 数归证明2cos(nx)能表示成关于2cosx的多项式,设为f(n) f(1)=x,f(2)=x^2-2(其中的x就是2cosx) 假设n=1~k时均成立(k>=3) 当n=k+1时 由cos((k+1)x)=cos(kx)cos(x)-sin(kx)sin(x) cos((k-1)x)=cos(kx)cos(x)

hihocoder1496(高维前缀和)

题意:给定N个数A1, A2, A3, ... AN,小Ho想从中找到两个数Ai和Aj(i ≠ j)使得乘积Ai × Aj × (Ai AND Aj)最大.其中AND是按位与操作. 第一行一个整数N(1<=N<=100,000) 第二行N个整数A1, A2, A3, ... AN (0 <= Ai <2^20) 分析: 尝试枚举and值z,那么问题就变成了找寻最大的x*y,使得x&y==z 把这个要求放宽一点,我们来寻找z是x&y子集的情况(这样肯定不会丢掉整体最优

Codeforces 449D:Jzzhu and Numbers(高维前缀和)

[题目链接] http://codeforces.com/problemset/problem/449/D [题目大意] 给出一些数字,问其选出一些数字作or为0的方案数有多少 [题解] 题目等价于给出一些集合,问其交集为空集的方案数, 我们先求交集为S的方案数,记为dp[S],发现处理起来还是比较麻烦, 我们放缩一下条件,求出交集包含S的方案数,记为dp[S], 我们发现dp[S],是以其为子集的方案的高维前缀和, 我们逆序求高维前缀和即可,之后考虑容斥,求出交集为0的情况, 我们发现这个容斥

SPOJ Time Limit Exceeded(高维前缀和)

[题目链接] http://www.spoj.com/problems/TLE/en/ [题目大意] 给出n个数字c,求非负整数序列a,满足a<2^m 并且有a[i]&a[i+1]=0,对于每个a[i],要保证a[i]不是c[i]的倍数, 求这样的a[i]序列的个数 [题解] 我们用dp[i]表示以i为结尾的方案数, 我们发现要满足a[i]&a[i+1]=0,则dp[i]是从上一次结果中所有满足i&j=0的地方转移过来的 i&j=0即i&(~j)=i,即i为~

三级菜单-2018.2.21

根据老男孩课程以及网上的代码,自行打出的代码,虽然参照的比较多,嘿嘿嘿 #_author_:"Bushii" #data:2018/2/21 menu= { '山东' : { '青岛' : ['四方','黄岛','崂山','李沧','城阳'], '济南' : ['历城','槐荫','高新','长青','章丘'], '烟台' : ['龙口','莱山','牟平','蓬莱','招远'] }, '江苏' : { '苏州' : ['沧浪','相城','平江','吴中','昆山'], '南京' :

2018/7/21 Python 爬虫学习

2018/7/21,这几天整理出来的一些Python 爬虫学习代码. import urllib2 response = urllib2.urlopen("http://baidu.com") html = response.read() print html 进一步,可以request import urllib2 req = urllib2.Request("http://www.baidu.com") response = urllib2.urlopen(re

2018/8/21 qbxt测试

2018/8/21 qbxt测试 期望得分:0? 实际得分:0 思路:manacher   会写模板但是不会用 qwq 听了某人的鬼话,直接输出0,然后就gg了 #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N = (int)2e6 + 10; typedef long long ll;