[链分治 重链剖分 FWT] BZOJ 4911 [Sdoi2017]切树游戏

13 篇文章 0 订阅
2 篇文章 0 订阅

我链分治是从immortalCO今年论文学来的
就是一个序列上能够维护的东西,把他搬到重链上,先处理好儿子重链的答案,然后把对这条重链上的影响累加在这条重链上
修改就爬重链一路改上去

然后就是套路 FWT一下就能加和乘了
注意0没有逆元

复杂度 O(mnlog2n) 实际上树链剖分是跑不满的
rank1 开心 O(∩_∩)O~~

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<vector>
#define pb push_back
using namespace std;

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 read(int &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
inline void read(char &x){
  for (x=nc();x!='Q' && x!='C';x=nc());
}

const int N=30005;
const int KK=128;

const int P=10007;
const int INV2=(P+1)>>1;

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;
      }
}

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

struct edge{
  int u,v,next;
}G[N<<1];
int head[N],inum;
#define V G[p].v
inline void add(int u,int v,int p){
  G[p].u=u; G[p].v=v; G[p].next=head[u]; head[u]=p;
}

int fat[N],top[N],size[N];
int depth[N];
inline void dfs(int u,int fa){
  fat[u]=fa; size[u]=1; depth[u]=depth[fa]+1;
  for (int p=head[u];p;p=G[p].next)
    if (V!=fa)
      dfs(V,u),size[u]+=size[V]; 
}
vector<int> p[N];
inline void find(int u,int fa,int z){
  top[u]=z; p[z].pb(u);
  int maxv=0,son=0;
  for (int p=head[u];p;p=G[p].next)
    if (V!=fa && size[V]>maxv)
      maxv=size[son=V];
  if (son) find(son,u,z);
  for (int p=head[u];p;p=G[p].next)
    if (V!=fa && V!=son)
      find(V,u,V);
}


const int M=70005;
int ncnt,rt[N],pos[N];
int ps[M],ls[M],rs[M];
int sum[M][KK],lval[M][KK],rval[M][KK],val[M][KK];

inline void upd(int x){
  int L=ls[x],R=rs[x];
  for (int i=0;i<K;i++){
    val[x][i]=(val[L][i]+val[R][i]+rval[L][i]*lval[R][i])%P;
    lval[x][i]=(lval[L][i]+lval[R][i]*sum[L][i])%P;
    rval[x][i]=(rval[R][i]+rval[L][i]*sum[R][i])%P;
    sum[x][i]=sum[L][i]*sum[R][i]%P;
  }
}

struct abcd{
  int x,y;
  abcd(){ }
  abcd(int num){
    if (num) x=num,y=0; else x=1,y=1;
  }
  abcd & operator *= (int a){
    if (a==0) y++; else x=x*a%P;
    return *this;
  }
  abcd & operator /= (int a){
    if (a==0) y--; else x=x*inv[a]%P;
    return *this;
  }
  int val(){
    return y?0:x;
  }
};

abcd base[N][KK];

inline void Build(int &x,int l,int r,int t){
  x=++ncnt;
  if (l==r){
    for (int i=0;i<K;i++)
      val[x][i]=lval[x][i]=rval[x][i]=sum[x][i]=base[p[t][l-1]][i].val();
    pos[p[t][l-1]]=x;
    return;
  }
  int mid=(l+r)>>1;
  Build(ls[x],l,mid,t); Build(rs[x],mid+1,r,t);
  upd(x); ps[ls[x]]=ps[rs[x]]=x;
}

int ans[KK],tmp[KK];

inline void Modify(int u){
  int t=top[u];
  if (fat[t])
    for (int j=0;j<K;j++)
      base[fat[t]][j]/=(lval[rt[t]][j]+kx[0][j])%P;
  for (int j=0;j<K;j++)
    ans[j]=(ans[j]+P-val[rt[t]][j])%P;

  int x=pos[u];
  for (int i=0;i<K;i++)
    val[x][i]=lval[x][i]=rval[x][i]=sum[x][i]=base[u][i].val();
  x=ps[x];
  while (x)
    upd(x),x=ps[x];

  if (fat[t])
    for (int j=0;j<K;j++)
      base[fat[t]][j]*=(lval[rt[t]][j]+kx[0][j])%P;
  for (int j=0;j<K;j++)
    ans[j]=(ans[j]+val[rt[t]][j])%P;
}

int n,vv[N];

int lst[N],pnt;
inline bool cmp(int x,int y){
  return depth[x]>depth[y];
}

int main(){
  int x,y,Q; char order;
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  read(n); read(K); int t=1; while (t<K) t<<=1; K=t;
  Pre(K);
  for (int i=1;i<=n;i++){
    read(x); vv[i]=x;
    for (int j=0;j<K;j++) base[i][j]=abcd(kx[x][j]);
  }
  for (int i=1;i<n;i++)
    read(x),read(y),add(x,y,++inum),add(y,x,++inum);
  dfs(1,0); find(1,0,1);
  for (int i=1;i<=n;i++) if (top[i]==i) lst[++pnt]=i;
  sort(lst+1,lst+pnt+1,cmp);
  for (int i=1;i<=pnt;i++){
    int x=lst[i];
    Build(rt[x],1,p[x].size(),x);
    if (fat[x]){
      int f=fat[x];
      for (int j=0;j<K;j++)
    base[f][j]*=(lval[rt[x]][j]+kx[0][j])%P;
    }
    for (int j=0;j<K;j++)
      ans[j]=(ans[j]+val[rt[x]][j])%P;
  }

  read(Q);
  while (Q--){
    read(order); read(x);
    if (order=='C'){
      read(y);
      for (int j=0;j<K;j++)
    base[x][j]/=kx[vv[x]][j];
      vv[x]=y;
      for (int j=0;j<K;j++)
    base[x][j]*=kx[vv[x]][j];
      while (x!=0)
    Modify(x),x=fat[top[x]];
    }else if (order=='Q'){
      for (int j=0;j<K;j++) tmp[j]=ans[j];
      FWT(tmp,K,0);
      printf("%d\n",tmp[x]);
    }
  }
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值