[FWT && 链分治] BZOJ4911.[Sdoi2017]切树游戏

本文介绍了一种基于树状结构的复杂算法实现,利用FWT变换等技术处理节点间的关系,实现了高效的路径更新与查询操作。该算法适用于解决大规模树形结构上的动态问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

可以看immortalCO大佬的博客

我是参考了Manchery的代码…

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

const int N=30010,M=310,P=10007,inv2=P+1>>1;

inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}

inline void rea(int &x){
    char c=nc(); x=0;
    for(;c>'9'||c<'0';c=nc());for(;c>='0'&&c<='9';x=x*10+c-'0',c=nc());
}

int n,k,q;

int v[N];
int e[M][M],inv[P+5];

struct U{
    int x,y;
    U(int a=0){
        if(a) x=a,y=0;
        else y=1,x=1;
    }
    friend U operator *(U a,int b){
        if(!b) a.y++; else a.x=a.x*b%P; 
        return a;
    }
    friend U operator /(U a,int b){
        if(!b) a.y--; else a.x=a.x*inv[b]%P;
        return a;
    }
    int val(){
        return y?0:x;
    }
};

U a[N][M];

inline void FWT(int *a,int n,int r){
    for(int i=1;i<n;i<<=1)
        for(int j=0;j<n;j+=i<<1)
            for(int k=0;k<i;k++){
                int x=a[j+k],y=a[j+k+i];
                if(r) a[j+k]=(x+y)%P,a[j+k+i]=(x+P-y)%P;
                else a[j+k]=(x+y)*inv2%P,a[j+k+i]=(x+P-y)*inv2%P;
            } 
}

inline void Pre(int t){
    for(int i=0;i<t;i++){
        e[i][i]=1; FWT(e[i],t,1);
    }
    inv[1]=1;
    for(int i=2;i<P;i++) inv[i]=(P-P/i)*inv[P%i]%P; 
} 

int cnt,G[N],fa[N],top[N],dpt[N],size[N],son[N];
struct edge{
    int t,nx;
}E[N<<1];

inline void Insert(int x,int y){
    E[++cnt].t=y; E[cnt].nx=G[x]; G[x]=cnt;
    E[++cnt].t=x; E[cnt].nx=G[y]; G[y]=cnt;
}

void dfs1(int x,int f){
    fa[x]=f; dpt[x]=dpt[f]+1; size[x]=1;
    for(int i=G[x];i;i=E[i].nx)
        if(E[i].t!=f){
            dfs1(E[i].t,x);
            if(size[E[i].t]>size[son[x]]) son[x]=E[i].t;
            size[x]+=size[E[i].t];
        }
}

vector<int> p[N];

int Q[N];

void dfs2(int x,int t){
    top[x]=t; p[t].push_back(x);
    if(x==t) Q[++*Q]=x;
    if(son[x]) dfs2(son[x],t);
    for(int i=G[x];i;i=E[i].nx)
        if(E[i].t!=fa[x] && son[x]!=E[i].t) dfs2(E[i].t,E[i].t);
}

inline int cmp(const int &a,const int &b){
    return dpt[a]>dpt[b];
}

int icnt,rt[N],ps[N];
int ls[N*3],rs[N*3],ft[N*3],val[N*3][M],lv[N*3][M],rv[N*3][M],pro[N*3][M];

inline void Up(int g){
    for(int i=0;i<k;i++){
        val[g][i]=(val[ls[g]][i]+val[rs[g]][i]+rv[ls[g]][i]*lv[rs[g]][i])%P;
        lv[g][i]=(lv[ls[g]][i]+pro[ls[g]][i]*lv[rs[g]][i])%P;
        rv[g][i]=(rv[rs[g]][i]+pro[rs[g]][i]*rv[ls[g]][i])%P;
        pro[g][i]=pro[ls[g]][i]*pro[rs[g]][i]%P;
    }
}

void Build(int &g,int l,int r,int x){
    g=++icnt;
    if(l==r){
        for(int i=0;i<k;i++)
            val[g][i]=lv[g][i]=rv[g][i]=pro[g][i]=a[p[x][l-1]][i].val();
        ps[p[x][l-1]]=g;
        return ;
    } 
    int mid=l+r>>1;
    Build(ls[g],l,mid,x); Build(rs[g],mid+1,r,x);
    Up(g); ft[ls[g]]=ft[rs[g]]=g;
}

int ans[M];

inline void Modify(int x){
    int cur=ps[x],y=top[x];
    if(fa[y]){
        for(int i=0;i<k;i++)
            a[fa[y]][i]=a[fa[y]][i]/((lv[rt[y]][i]+e[0][i])%P);
    }
    for(int i=0;i<k;i++)
        ans[i]=(ans[i]+P-val[rt[y]][i])%P;
    for(int i=0;i<k;i++)
        pro[cur][i]=lv[cur][i]=rv[cur][i]=val[cur][i]=a[x][i].val();
    cur=ft[cur];
    while(cur) Up(cur),cur=ft[cur];
    if(fa[y]){
        for(int i=0;i<k;i++)
            a[fa[y]][i]=a[fa[y]][i]*((lv[rt[y]][i]+e[0][i])%P);
    }
    for(int i=0;i<k;i++)
        ans[i]=(ans[i]+val[rt[y]][i])%P;
}

int tmp[M];

int main(){
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
    rea(n); rea(k);
    int t=1; while(t<k) t<<=1; k=t;
    Pre(k);
    for(int i=1;i<=n;i++){
        rea(v[i]);
        for(int j=0;j<k;j++) a[i][j]=U(e[v[i]][j]);
    }
    for(int i=1,x,y;i<n;i++)
        rea(x),rea(y),Insert(x,y);
    dfs1(1,0); dfs2(1,1);
    sort(Q+1,Q+1+*Q,cmp);
    for(int i=1;i<=*Q;i++){
        int x=Q[i];
        Build(rt[x],1,p[x].size(),x);
        //FWT(val[rt[x]],k,0); for(int j=0;j<k;j++) printf("%d ",val[rt[x]][j]); putchar('\n'); 
        for(int j=0;j<k;j++)
            ans[j]=(ans[j]+val[rt[x]][j])%P;
        if(fa[x]){
            for(int j=0;j<k;j++)
                a[fa[x]][j]=a[fa[x]][j]*((lv[rt[x]][j]+e[0][j])%P);
        }
    }
    //FWT(ans,k,0); for(int i=0;i<k;i++) printf("%d ",ans[i]); putchar('\n');
    rea(q);
    while(q--){
        char opt; while((opt=nc())!='Q' && opt!='C');
        if(opt=='C'){
            int x,y; rea(x); rea(y);
            for(int i=0;i<k;i++)
                a[x][i]=a[x][i]/e[v[x]][i];
            v[x]=y;
            for(int i=0;i<k;i++) a[x][i]=a[x][i]*e[v[x]][i];
            while(x)
                Modify(x),x=fa[top[x]];
        }
        else{
            int x; rea(x);
            for(int i=0;i<k;i++) tmp[i]=ans[i];
            FWT(tmp,k,0);
            printf("%d\n",tmp[x]);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值