ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

牛客挑战赛39 树与异或 离线 树上莫队 树状数组 约数

2020-04-22 20:03:39  阅读:251  来源: 互联网

标签:约数 39 num int 离线 ++ dfn MAXN wy


LINK:树与异或

这种套路题还是得多写写。

第一问 直接树上莫队即可(不过这个板子也容易遗忘 推荐dfs序上搞 树分块总觉得比较难写...

第二问 询问树上路径上点权为z的倍数的点的个数.

Analysis:可以考虑暴力了。暴力枚举z 然后统计询问的答案。

不过每次要将z的倍数的点要加到数据结构中 然后就可以快速查询了 显然树状数组+dfs序就可以。

考虑这样做的复杂度 可以发现一个点最多被加入到树状数组里自己的因数个数次。

而一个数字的约数最多 \(n^{\frac{1}{2}}\),\(log^2\),\(n^{\frac{1}{3}}\). 有这几个数量级 精确一点的话第三个都是比较松的上界 。

所以 再乘以一个log 也是可以过的。

输出的一个小细节写挂了 写的时候一定要清醒哇.

再提供一个 分块分治的做法:对于z>sqrt(nlogn) 可以设数组 f[i][j]表示由根到i路径上为j的倍数的个数。

然后对于z>sqrt(nlogn) 此时使用树状数组。

前者 可以简单的发现是nsqrt(nlogn)的 后者 nn/sqrt(nlogn)logn=nsqrt(nlogn)的。

这个方法也是可以过的。

const int MAXN=100010;
int T,len,cnt,Q,n,ans,S,num,maxx;
int a[MAXN],w[MAXN],d[MAXN],ans1[MAXN],ans2[MAXN];
int f[MAXN][20],Log[MAXN],dfn[MAXN],las[MAXN],vis[MAXN];
int lin[MAXN],ver[MAXN<<1],nex[MAXN<<1],c[MAXN<<1],B[MAXN<<1],pos[MAXN<<1];
struct wy{int l,r,id,op;}t[MAXN],s[MAXN*3];
vector<int>g[MAXN];
inline void add(int x,int y)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
}
inline void dfs(int x,int fa)
{
    d[x]=d[fa]+1;f[x][0]=fa;
    dfn[x]=++cnt;pos[cnt]=x;
    rep(1,Log[d[x]],i)f[x][i]=f[f[x][i-1]][i-1];
    go(x)if(tn!=fa)dfs(tn,x);
    las[x]=++cnt;pos[cnt]=x;
}
inline int LCA(int x,int y)
{
    if(d[x]<d[y])swap(x,y);
    fep(Log[d[x]],0,i)if(d[f[x][i]]>=d[y])x=f[x][i];
    if(x==y)return x;
    fep(Log[d[x]],0,i)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
    return f[x][0];
}
inline int cmp(wy a,wy b){return B[a.l]==B[b.l]?a.r<b.r:a.l<b.l;}
inline int cmp1(wy a,wy b){return a.r<b.r;}
inline void del(int x){ans=ans-(--w[x]==0);}
inline void add(int x){ans=ans+(++w[x]==1);}
inline void change(int x)
{
    if(vis[x])del(a[x]),vis[x]=0;
    else add(a[x]),vis[x]=1;
}
inline void add1(int x,int y)
{
    while(x<=num)
    {
        c[x]+=y;
        x+=x&(-x);
    }
}
inline int ask(int x)
{
    int cnt=0;
    while(x)
    {
        cnt+=c[x];
        x-=x&(-x);
    }
    return cnt;
}
int main()
{
    freopen("1.in","r",stdin);
    get(T);
    rep(1,T,TT)
    {
        rep(1,n,i)
        {
            lin[i]=vis[i]=0;
            rep(1,Log[d[i]],j)f[i][j]=0;//可以发现 清空是必要的.
        }
        get(n);ans=num=maxx=cnt=len=0;
        rep(1,n,i)
        {
            get(a[i]);
            maxx=max(maxx,a[i]);
            g[a[i]].pb(i);
            w[a[i]]=0;
        }
        rep(2,n,i)
        {
            Log[i]=Log[i>>1]+1;
            int get(x),get(y);
            add(x,y);add(y,x);
        }
        dfs(1,0);
        get(Q);
        rep(1,Q,i)
        {
            ans1[i]=ans2[i]=0;
            int get(x),get(y),get(z);
            if(dfn[x]>dfn[y])swap(x,y);
            int lca=LCA(x,y);
            if(lca==x)t[i]=(wy){dfn[x],dfn[y],i,0};
            else t[i]=(wy){las[x],dfn[y],i,lca};
            s[++num]=(wy){y,z,i,1};
            maxx=max(maxx,z);
            if(lca==x){if(f[x][0])s[++num]=(wy){f[x][0],z,i,-1};}
            else
            {
                s[++num]=(wy){x,z,i,1};
                s[++num]=(wy){lca,z,i,-2};
            }
        }
        S=(int)sqrt(2*n*1.0)+1;
        rep(1,cnt,i)B[i]=(i-1)/S+1;
        sort(t+1,t+1+Q,cmp);
        int L=1,R=0;
        rep(1,Q,i)
        {
            while(R<t[i].r)change(pos[++R]);
            while(R>t[i].r)change(pos[R--]);
            while(L<t[i].l)change(pos[L++]);
            while(L>t[i].l)change(pos[--L]);
            if(t[i].op)change(t[i].op);
            ans1[t[i].id]=ans;
            if(t[i].op)change(t[i].op);
        }
        //求树上路径和为z的倍数的个数 树状数组做.
        sort(s+1,s+1+num,cmp1);
        int flag=1;
        rep(1,maxx,i)
        {
            if(s[flag].r==i)
            {
                for(int j=i;j<=maxx;j+=i)
                {
                    for(ui k=0;k<g[j].size();++k)
                    {
                        int tn=g[j][k];
                        add1(dfn[tn],1);
                        add1(las[tn]+1,-1);
                    }
                }
                while(s[flag].r==i&&flag<=num)
                {
                    ans2[s[flag].id]+=s[flag].op*ask(dfn[s[flag].l]);
                    if(s[flag].op==-2)if(a[s[flag].l]%i==0)++ans2[s[flag].id];
                    ++flag;
                }
                for(int j=i;j<=maxx;j+=i)
                {
                    for(ui k=0;k<g[j].size();++k)
                    {
                        int tn=g[j][k];
                        add1(dfn[tn],-1);
                        add1(las[tn]+1,1);
                    }
                }
            }
            g[i].clear();
        }
        printf("Case #%d:\n",TT);
        rep(1,Q,i)put(ans1[i]^ans2[i]);
    }
    return 0;
}

标签:约数,39,num,int,离线,++,dfn,MAXN,wy
来源: https://www.cnblogs.com/chdy/p/12755520.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有