牛客挑战赛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:\n",TT);
        rep(1,Q,i)put(ans1[i]^ans2[i]);
    }
    return 0;
}
上一篇:Python项目生成所有依赖包的清单


下一篇:实验三 Linux系统用户管理及VIM配置