[点分树] Codechef December Challenge 2017. Chef, Leonardo And Queries

我好像只会套路数据结构题了…

暴力上点分树,这样的递推形式可以用矩阵表示,那么对于点 i ,它的线段树上区间覆盖上 fdis(i,u)×A 的表示,其中 fi 表示转移矩阵的 i 次方,A 就是题目给出的 a,b 构成的矩阵。

然后询问点 i 管辖的点 j 的时候,线段树中询问得到的结果乘上 fdis(i,j) 就好了

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <ctime>
#include <assert.h>

using namespace std;

const int N=300010,P=1e9+7;

struct mat{
  int a[2][2];
  mat(){a[0][0]=a[0][1]=a[1][0]=a[1][1]=0; }
  int &val(){ return a[0][0]; }
  friend mat operator *(mat a,mat b){
    mat ret;
    ret.a[0][0]=(1LL*a.a[0][0]*b.a[0][0]+1LL*a.a[0][1]*b.a[1][0])%P;
    ret.a[0][1]=(1LL*a.a[0][0]*b.a[0][1]+1LL*a.a[0][1]*b.a[1][1])%P;
    ret.a[1][0]=(1LL*a.a[1][0]*b.a[0][0]+1LL*a.a[1][1]*b.a[1][0])%P;
    ret.a[1][1]=(1LL*a.a[1][0]*b.a[0][1]+1LL*a.a[1][1]*b.a[1][1])%P;
    return ret;
  }
  friend mat operator +(mat a,mat b){
    mat ret;
    ret.a[0][0]=(a.a[0][0]+b.a[0][0])%P;
    ret.a[0][1]=(a.a[0][1]+b.a[0][1])%P;
    ret.a[1][0]=(a.a[1][0]+b.a[1][0])%P;
    ret.a[1][1]=(a.a[1][1]+b.a[1][1])%P;
    return ret;
  }
  friend mat operator -(mat a,mat b){
    mat ret;
    ret.a[0][0]=(a.a[0][0]-b.a[0][0])%P;
    ret.a[0][1]=(a.a[0][1]-b.a[0][1])%P;
    ret.a[1][0]=(a.a[1][0]-b.a[1][0])%P;
    ret.a[1][1]=(a.a[1][1]-b.a[1][1])%P;
    return ret;
  }
  };

inline int mul(mat a,mat b){
  return (1LL*a.a[0][0]*b.a[0][0]+1LL*a.a[0][1]*b.a[1][0])%P;
}

mat f[N],u;

int t;

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(); x=0;
  for(;c>'9'||c<'0';c=nc());for(;c>='0'&&c<='9';x=x*10+c-'0',c=nc());
}

int n,q,cnt,G[N];
struct edge{
  int t,nx;
}E[N<<1];

inline void addedge(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;
}

int fa[N][20],dpt[N];
inline int lca(int x,int y){
  if(dpt[x]<dpt[y]) swap(x,y);
  for(int i=19;~i;i--)
    if(dpt[fa[x][i]]>=dpt[y]) x=fa[x][i];
  if(x==y) return x;
  for(int i=19;~i;i--)
    if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
  return fa[x][0];
}

inline int dist(int x,int y){
  return dpt[x]+dpt[y]-2*dpt[lca(x,y)];
}

int deep[N];

namespace T{
  int cnt,G[N],fa[N];
  struct edge{
    int t,nx;
  }E[N<<1];

  int ls[N*50],rs[N*50]; mat tag[N*50];

  int t,root[N],droot[N];
  int Dis[N][30];

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

  void clear(){
    t=cnt=0;
    for(int i=1;i<=n;i++) root[i]=droot[i]=G[i]=fa[i]=0;
    for(int i=1;i<=n;i++)
      for(int j=0;j<=20;j++) Dis[i][j]=0;
  }

  void Pre(){
    for(int i=1;i<=n;i++)
      for(int x=fa[i],j=1;x;x=fa[x],j++)
    Dis[i][j]=dist(i,x);
  }

  void Add(int &g,int l,int r,int L,int R,mat c){
    if(!g) g=++t,tag[g]=mat(),ls[g]=rs[g]=0;
    if(l==L && r==R){
      tag[g]=tag[g]+c; return ; 
    }
    int mid=L+R>>1;
    if(r<=mid) Add(ls[g],l,r,L,mid,c);
    else if(l>mid) Add(rs[g],l,r,mid+1,R,c);
    else Add(ls[g],l,mid,L,mid,c),Add(rs[g],mid+1,r,mid+1,R,c);
  }

  mat Query(int g,int x,int L,int R){
    mat ret;
    while(g){
      ret=ret+tag[g];
      if(L==R) break;
      int mid=L+R>>1;
      if(x<=mid) g=ls[g],R=mid; else g=rs[g],L=mid+1;
    }
    return ret;
    /*if(!g) return mat();
    if(L==R) return tag[g];
    int mid=L+R>>1;
    if(x<=mid) return Query(ls[g],x,L,mid)+tag[g];
    else return Query(rs[g],x,mid+1,R)+tag[g];*/
  }

  inline void Add(int x,int k,mat c){
    Add(root[x],0,min(k,deep[x]),0,deep[x],c);
    for(int i=x,j=1;fa[i];i=fa[i],j++){
      int dis=Dis[x][j];
      if(dis>k) continue;
      Add(droot[i],0,min(k-dis,deep[fa[i]]),0,deep[fa[i]],c*f[dis]);
      Add(root[fa[i]],0,min(k-dis,deep[fa[i]]),0,deep[fa[i]],c*f[dis]);
    }
  }

  inline int Query(int x){
    int ret=Query(root[x],0,0,deep[x]).val();
    for(int i=x,j=1;fa[i];i=fa[i],j++){
      int dis=Dis[x][j];
      assert(dis<=deep[fa[i]]);
      ret=(ret+mul((Query(root[fa[i]],dis,0,deep[fa[i]])-Query(droot[i],dis,0,deep[fa[i]])),f[dis]))%P;
    }
    return (ret+P)%P;
  }

  void dfs(int x){
    deep[x]=1;
    for(int i=G[x];i;i=E[i].nx)
      dfs(E[i].t),deep[x]=max(deep[x],deep[E[i].t]+1);
  }
}

int root,imax,size,vis[N];

void Size(int x,int f){
  size++; for(int i=G[x];i;i=E[i].nx)if(E[i].t!=f && !vis[E[i].t]) Size(E[i].t,x);
}

int Root(int x,int f){
  int ret=1,cmax=0;
  for(int i=G[x];i;i=E[i].nx)
    if(E[i].t!=f && !vis[E[i].t]){
      int cur=Root(E[i].t,x);
      cmax=max(cmax,cur);
      ret+=cur;
    }
  cmax=max(cmax,size-ret);
  if(cmax<imax) imax=cmax,root=x;
  return ret;
}

int Deep(int x,int f){
  int ret=0;
  for(int i=G[x];i;i=E[i].nx)
    if(E[i].t!=f && !vis[E[i].t]) ret=max(Deep(E[i].t,x),ret);
  return ret+1;
}

void build(int x,int f){
  size=0; Size(x,0);
  imax=1<<30; Root(x,0); deep[root]=Deep(root,0);
  if(f) T::addedge(f,root);
  vis[root]=1; int croot=root;
  for(int i=G[root];i;i=E[i].nx)
    if(!vis[E[i].t]) build(E[i].t,croot);
}

void dfs(int x,int f){
  dpt[x]=dpt[f]+1;
  fa[x][0]=f; for(int i=1;i<=19;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
  for(int i=G[x];i;i=E[i].nx)
    if(E[i].t!=f) dfs(E[i].t,x);
}

void PutAns(int x){
  if(x>=10) PutAns(x/10); putchar(x%10+'0');
}

int main(){
  u.a[0][0]=u.a[0][1]=u.a[1][0]=1;
  f[0].a[0][0]=f[0].a[1][1]=1;
  for(int i=1;i<=300000;i++) f[i]=f[i-1]*u;
  read(t);
  while(t--){
    read(n); read(q); cnt=0;
    for(int i=1;i<=n;i++) G[i]=vis[i]=0;
    T::clear();
    for(int i=1,x,y;i<n;i++)
      read(x),read(y),addedge(x,y);
    build(1,0); dfs(1,0); T::Pre();
    while(q--){
      int opt,v,k,a,b;
      read(opt); read(v);
      if(opt==1){
    read(k); read(a); read(b);
    mat c; c.a[0][0]=a; c.a[1][0]=c.a[0][1]=b-a;
    T::Add(v,k,c);
      }
      else
    PutAns(T::Query(v)),putchar('\n');
      //printf("%d\n",T::Query(v));
    }
  }
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值