zoukankan      html  css  js  c++  java
  • 牛客挑战赛39 树与异或 离线 树上莫队 树状数组 约数

    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:
    ",TT);
            rep(1,Q,i)put(ans1[i]^ans2[i]);
        }
        return 0;
    }
    
  • 相关阅读:
    机器学习——线性回归
    系统课程
    C++——数据结构之链表
    PHP检测json格式数据
    如何搭建git服务器
    ucenter通信实现同步登录、同步退出(详细)
    获取ip地址
    JQuery对象与DOM对象的区别与转换
    php导出数据到csv文件
    [转载]php中sleep,flush,ob_flush函数介绍
  • 原文地址:https://www.cnblogs.com/chdy/p/12755520.html
Copyright © 2011-2022 走看看