FZU2277 Change: 线段树-第八届福建省大学生程序设计竞赛

(有任何问题欢迎留言或私聊 && 欢迎交流讨论哦

Catalog

Problem:Portal传送门

 原题目描述在最下面。
 区间更新,单点求和。数据范围1e5。

Solution:

 听说要用读入挂,然后我不用读入挂也过了!不加读入挂 1390ms 1390 m s ,加读入挂 828ms 828 m s ,你们可以去 vj v j 上看我的代码哟。
 节点 u u x,节点 u u 的儿子u xy x − y ,节点 u u 的孙子u x2×y x − 2 × y ,依次类推。
 其实我们可以在给 u u 的子树区间更新的时候,可以更新x+dep[u]×y数值,这样就是普通的区间更新,对于查询我们再特殊处理一下查询就是 (x+dep[]×y)dep[i]×y ∑ ( x + d e p [ ] × y ) − d e p [ i ] × ∑ y
 这样的话我们线段树每个节点保存两个值 sum1 s u m 1 sum2 s u m 2 . sum1 s u m 1 就是所有 (x+dep[u]×y) ( x + d e p [ u ] × y ) 的和,而 sum2 s u m 2 就是所有 y y 的和。普通区间更新,查询答案时输出sum1sum2×dep[u]即可。

AC_Code:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define lson rt<<1
#define rson rt<<1|1
#define read(x) scanf("%d", &x)
using namespace std;
typedef long long LL;
const int N = 600000+7;
const int MXM = 3e5+7;
const LL MOD = 1000000007;
int n, q;
struct lp{
    int l,r;
    LL sum1,sum2;
}cw[N<<2];
int tid[N],rtid[N],dep[N];
struct lh{
  int v, nex;
}edge[N];
int head[N], tot, top;
inline void insert(int a,int b){
  edge[++tot].v=b;edge[tot].nex=head[a];
  head[a]=tot;
}
void dfs(int u,int dd){
  tid[u] = ++top;
  dep[u]=dd;
  for(int i=head[u];~i;i=edge[i].nex){
    int v = edge[i].v;
    dfs(v,dd+1);
  }
  rtid[u]=top;
}
void build(int l,int r,int rt){
  cw[rt].l=l;cw[rt].r=r;
  cw[rt].sum1=cw[rt].sum2=0;
  if(l==r)return;
  int mid = (l+r)>>1;
  build(l,mid,lson);build(mid+1,r,rson);
}
inline void Add(LL &a, LL b){
  a += b;
  while(a>=MOD)a-=MOD;
}
void push_down(int rt){
  LL a = cw[rt].sum1, b = cw[rt].sum2;
  if(a)Add(cw[lson].sum1, a);
  if(a)Add(cw[rson].sum1, a);
  if(b)Add(cw[lson].sum2, b);
  if(b)Add(cw[rson].sum2, b);
  cw[rt].sum1 = cw[rt].sum2 = 0;
}
void update(int L,int R,LL y,LL z,int rt){
  int l = cw[rt].l,r=cw[rt].r,mid=(l+r)>>1;
  if(L<=l&&r<=R){
    Add(cw[rt].sum1, y);
    Add(cw[rt].sum2, z);
    return;
  }
  push_down(rt);
  if(L>mid)update(L,R,y,z,rson);
  else if(R<=mid)update(L,R,y,z,lson);
  else{
    update(L,mid,y,z,lson),update(mid+1,R,y,z,rson);
  }
}
LL query(int p,int k,int rt){
  if(cw[rt].l==cw[rt].r){
    return ((cw[rt].sum1-cw[rt].sum2*k%MOD)+MOD)%MOD;
  }
  push_down(rt);
  int mid = (cw[rt].l+cw[rt].r)>>1;
  if(p<=mid)return query(p,k,lson);
  return query(p,k,rson);
}
int main(){
  int tim;read(tim);
  while(tim--){
    read(n);
    for(int i=0;i<=n;++i)head[i]=-1;
    tot = -1;top = 0;
    for(int i = 2, u; i <= n; ++i){
      read(u);
      insert(u,i);
    }
    dfs(1,1);
    read(q);
    build(1,n,1);
    while(q--){
      int op,x;
      int y,z;
      read(op);
      if(op == 1){
        read(x);read(y);read(z);
        update(tid[x],rtid[x],(y*1LL+dep[x]*1LL*z%MOD)%MOD,z,1);
      }else{
        read(x);
        printf("%lld\n", query(tid[x],dep[x],1));
      }
    }
  }
  return 0;
}


Problem Description:

这里写图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值