UOJ Logo kczno1的博客

博客

Count on a tree 2的点分+暴力bitset做法

2017-04-26 23:15:35 By kczno1

点分,处理每个点到根的bitset,询问时或一下两个bitset。

但是由于可持久化树存储的bitset或运算会很慢,在q*n/32本来就是时间瓶颈的情况下显然是不好的,

所以我想不用可持久化bitset,而是直接每个点都新开个bitset。

注意到实际上bitset只用当前分治处理的这棵树的信息,所以离散化一下,大小就变成O(树的大小)了

于是复杂度就是O(n^2/32+n^2/2/32+..n^2/n/32)=O(n^2/32)。

于是我没有把int全部替换成unsigned short,也没有在分治的树小的时候暴力等等,就拿到了bzoj rank1。

#include<bits/stdc++.h>
using std::sort;

#define us unsigned short
#define ui unsigned int
const int N=40010;
struct edge
{
    int to,next;
}l[N<<1];int e;
int a[N],t[N];

int n,i,x,y;
bool xiao(int *x,int *y)
{
    return *x<*y;
}
void lisan()
{
    static int *q[N],top;
    for(i=1;i<=n;++i)q[i]=a+i;
    sort(q+1,q+n+1,xiao);
    int now=-1;
    for(i=1;i<=n;++i)
    {
        if(*q[i]!=now){now=*q[i];++top;}
        *q[i]=top;
    }
}

us t_f[N],deep[N];
ui *b[17][N],**now_b,*X,*Y;
us U[N];

bool vis[N];
us q[N],head,tail,sz[N];
us cnt[N],mx;
us f[N];
void build(us rt,us dep,us fr)
{
    for(;mx;--mx)cnt[mx]=0; 
    f[q[tail=1]=rt]=0;
    for(head=1;head<=tail;++head)
    {
        x=q[head];sz[x]=1;
        cnt[a[x]]=1;if(a[x]>mx)mx=a[x];
        for(i=t[x];y=l[i].to;i=l[i].next)
        if(!vis[y]&&y!=f[x])
            f[q[++tail]=y]=x;
    }

    for(i=2;i<=mx;++i)cnt[i]+=cnt[i-1];
    while(x=q[--head])
    {
        if((sz[x]<<1)>=tail)break;
        sz[f[x]]+=sz[x];
    }
    rt=x;
    t_f[rt]=fr;deep[rt]=dep;
    vis[rt]=1;

    now_b=b[dep];
    f[q[tail=1]=rt]=0;
    us u=(cnt[mx]>>5)+1; U[rt]=u;
    for(head=1;head<=tail;++head)
    {
        x=q[head];a[x]=cnt[a[x]]; 
        now_b[x]=new ui [u];
        if(head>1) for(i=0;i<u;++i)now_b[x][i]=now_b[f[x]][i];
        else for(i=0;i<u;++i)now_b[x][i]=0;
        now_b[x][a[x]>>5]|=1<<(a[x]&31);
        for(i=t[x];y=l[i].to;i=l[i].next)
        if(!vis[y]&&y!=f[x])
        {
            f[y]=x;q[++tail]=y;
        }
    }
    ++dep;
    for(int i=t[rt];y=l[i].to;i=l[i].next)
    if(!vis[y]) build(y,dep,rt);
}

us get_lca(us x,us y)
{
    while(x!=y)
    if(deep[x]>deep[y])x=t_f[x];
    else y=t_f[y];
    return x;
}

namespace kcz
{
    const int U=1<<16;
    us f[U+1];
    void init()
    {
        for(i=1;i<=U;++i)f[i]=f[i-(i&-i)]+1; 
    }
    us count(ui x)
    {
        return f[x&(U-1)]+f[x>>16];
    }
};

const int ch_top=5000000;
char ch[ch_top],*now_r=ch;
void read(int &x)
{
    while(*now_r<'-')++now_r;
    if(*now_r=='-')
    {
      for(x=*++now_r-'0';*++now_r>='0';) x=(x<<1)+(x<<3)+*now_r-'0';
      x=-x;
    }
    else
    {
        for(x=*now_r-'0';*++now_r>='0';) x=(x<<1)+(x<<3)+*now_r-'0';
    }
}

us ans;

int main()
{
    freopen("1.in","r",stdin);freopen("1.out","w",stdout);
    int q;
    fread(ch,1,ch_top,stdin);
    read(n);read(q);
    for(i=1;i<=n;++i)read(a[i]);
    lisan();
    for(i=1;i<n;++i)
    {
        read(x);read(y);
        l[++e]=(edge){y,t[x]};t[x]=e;
        l[++e]=(edge){x,t[y]};t[y]=e;
    }
    build(1,0,0);
    kcz::init();
    while(q--)
    {
        read(x);read(y);//x=x^ans; 
        us lca=get_lca(x,y);
        X=b[deep[lca]][x];Y=b[deep[lca]][y];
        ans=0;
        for(i=0;i<U[lca];++i) ans+=kcz::count(X[i]|Y[i]);
        printf("%d\n",ans);
    }
}

评论

JOHNKRAM
$1+\frac{1}{2}+\frac{1}{3}+...+\frac{1}{n}=O(\ln n)$

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。