【纪中集训2019.3.26】动态半平面交

题目

描述 :

给出强制在线参数\(k\),树的大小\(n\),和每个点的点权\(a_i\);

\(m\)个询问,每个询问是$u ,d $ 的形式;

表示询问\(u\)为根的子树中,和\(u\)的距离不超过\(d\)的点的点权\(lcm\)

范围:

$k \in { 0,1 }  , 1 \le n  , q \le 100000  ,  a_i \le 10000000 $;

题解

  • \(lcm\)不太好合并,首先一定是找到一种合理的方式去表示\(lcm\)

  • 考虑每个因子\(p\),如果存在将\(p,p^2,p^3,\cdots\)看成不同的颜色,每种颜色存在的贡献是将答案乘以\(p\);

  • 对于每一种颜色,我们只需要维护一个树链并即可;

  • 对每个颜色维护一个\(set\),用来维护插入一个点树链并的修改;

  • 对于非强制在线的情况,只需要将询问按\(dep[u]+d\)排序,线段树维护单点修改子树查询;

  • 强制在线将线段树可持久化就好了;

  • 时间复杂度和空间复杂度:\(O(n \ log \ n \ log \ a_i)\) ;

    #include<bits/stdc++.h>
    #define pb push_back
    #define mod 998244353
    #define ll long long 
    using namespace std;
    const int N=100010,M=10000010;
    int n,m,k,a[N],b[N],o=1,hd[N],idx,st[N],ed[N],id[N],dep[N];
    int f[N<<1][20],lg[N<<1],pos[N<<1],bin[20],idx2;
    int sz,rt[N],mul[N*600],ls[N*600],rs[N*600];
    int vis[M],pr[M],pt,mn[M];
    struct Edge{int v,nt;}E[N<<1];
    set<int>s[N*30];
    set<int>::iterator I,I1,I2;
    map<int,int>hsh;
    char gc(){
      static char*p1,*p2,s[1000000];
      if(p1==p2)p2=(p1=s)+fread(s,1,1000000,stdin);
      return(p1==p2)?EOF:*p1++;
    }
    int rd(){
      int x=0;char c=gc();
      while(c<'0'||c>'9')c=gc();
      while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+c-'0',c=gc();
      return x; 
    }
    void adde(int u,int v){
      E[o]=(Edge){v,hd[u]};hd[u]=o++;
      E[o]=(Edge){u,hd[v]};hd[v]=o++;
    }
    void pre(){
      mn[1]=1;
      for(int i=2;i<=1e7;++i){
          if(!vis[i])mn[pr[++pt]=i]=i;
          for(int j=1,t;j<=pt&&i*pr[j]<=1e7;++j){
              vis[t=i*pr[j]]=1;mn[t]=pr[j];
              if(i%pr[j]==0)break;
          }
      }
    }
    void dfs(int u,int fa){
      f[pos[u]=++idx2][0]=u;
      id[st[u]=++idx]=u;
      dep[u]=dep[fa]+1;
      for(int i=hd[u];i;i=E[i].nt){
          int v=E[i].v;
          if(v==fa)continue;
          dfs(v,u);
          f[++idx2][0]=u;
      }
      ed[u]=idx;
    }
    int Min(int x,int y){return dep[x]<dep[y]?x:y;}
    void init(){
      lg[0]=-1;for(int i=1;i<=idx2;++i)lg[i]=lg[i>>1]+1;
      for(int i=bin[0]=1;i<=18;++i)bin[i]=bin[i-1]<<1;
      for(int i=1;i<=18;++i)
      for(int j=1;j+bin[i]-1<=idx2;++j){
          f[j][i]=Min(f[j][i-1],f[j+bin[i-1]][i-1]);
      }
    }
    int lca(int u,int v){
      int x=pos[u],y=pos[v]; 
      if(x>y)swap(x,y);
      int t=lg[y-x+1];
      return Min(f[x][t],f[y-bin[t]+1][t]);
    }
    bool cmp(const int&x,const int&y){return dep[x]<dep[y];}
    int get(int x){
      if(!hsh[x])return hsh[x]=++idx;
      return hsh[x];
    }
    int pw(int x,int y){
      int re=1;
      while(y){
          if(y&1)re=(ll)re*x%mod;
          y>>=1;x=(ll)x*x%mod;
      }
      return re;
    }
    void ins(int&k,int lst,int l,int r,int x,int y){
      mul[k=++sz]=(ll)mul[lst]*y%mod;
      ls[k]=ls[lst],rs[k]=rs[lst];
      if(l==r)return; 
      int mid=l+r>>1;
      if(x<=mid)ins(ls[k],ls[lst],l,mid,x,y);
      else ins(rs[k],rs[lst],mid+1,r,x,y);
    }
    int query(int k,int l,int r,int x,int y){
      if(l==x&&r==y)return mul[k];
      int mid=l+r>>1;
      if(y<=mid)return query(ls[k],l,mid,x,y);
      else if(x>mid)return query(rs[k],mid+1,r,x,y);
      else return (ll)query(ls[k],l,mid,x,mid)*query(rs[k],mid+1,r,mid+1,y)%mod;
    }
    int main(){
      freopen("half.in","r",stdin);
      freopen("half.out","w",stdout);
      pre();
      k=rd();n=rd();
      for(int i=1;i<=n;++i)a[i]=rd(),b[i]=i;
      for(int i=1;i<n;++i)adde(rd(),rd());
      dfs(1,0);
      init();
      sort(b+1,b+n+1,cmp);
      mul[0]=1;idx=0;
      for(int i=1;i<=n;++i){
          int u=b[i],d=dep[u],tmp=a[u];
          rt[d]=rt[dep[b[i-1]]];
          while(tmp!=1){
              int x=mn[tmp],ix=pw(x,mod-2),y,z=1;
              while(tmp%x==0&&(tmp/=x)){
                  y=get(z*=x);
                  s[y].insert(st[u]);
                  I=s[y].lower_bound(st[u]);
                  I1=I;if(I!=s[y].begin())--I1;
                  I2=I;++I2;
                  ins(rt[d],rt[d],1,n,st[u],x);
                  int u1=0,u2=0,t;
                  if(I!=s[y].begin()){
                      u1=id[*I1];t=lca(u1,u); 
                      ins(rt[d],rt[d],1,n,st[t],ix);
                  }
                  if(I2!=s[y].end()){
                      u2=id[*I2];t=lca(u2,u); 
                      ins(rt[d],rt[d],1,n,st[t],ix);
                  }
                  if(u1&&u2){
                      t=lca(u1,u2);
                      ins(rt[d],rt[d],1,n,st[t],x);
                  }
              }
          }
      }
      for(int i=dep[b[n]]+1;i<=n;++i)rt[i]=rt[i-1];
      m=rd();
      int ans=0;
      for(int i=1,u,d;i<=m;++i){
          u=rd()^(k*ans);d=min(n,dep[u]+rd()^(k*ans));
          ans=query(rt[d],1,n,st[u],ed[u]);
          printf("%d\n",ans);
      }
      return 0;
    }

转载于:https://www.cnblogs.com/Paul-Guderian/p/10602136.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值