【LeetCode LCP05】发LeetCoin (dfs序+线段树)

题目

题目链接
题目概括:给个树,要求实现3种操作:子树修改,单点修改,子树和查询。节点数N<5e4,操作次数Q<5e4

思路

  1. 这玩意第一反应:线段树啊woc
  2. 但是线段树好麻烦,所以不想写线段树
  3. 所以一开始的思路是直接用线段树的lazy数组的思路(需要时再修改),但发现复杂度是O(树深度),复杂度不稳定,容易被卡成O(NQ)。
  4. 结果还是要用线段树。
  5. 线段树的树深度稳定为logn,所以通过DFS序将问题转化为区间修改区间查询问题,线段树解决。O(Q logN)
  6. (当然能用线段树也能用树状数组,都挺香)

代码

const int N=50005;
const int MO=1e9+7;
class Solution {
public:
    //邻接表
    struct ss{int to,nex;}edge[N];  int head[N],ecnt;
    void add(int x,int y) {edge[++ecnt]=(ss){y,head[x]};head[x]=ecnt;}
    //dfs序
    int tot,id[N];
    void dfs(int pos){          id[pos]=++tot;
        for(int i=head[pos];i;i=edge[i].nex) dfs(edge[i].to);   return ;
    }
    //线段树模板
    long long s[4*N+10],a[4*N+10];//s表示单点值,a表示区间加值
    inline void up(int pos){s[pos]=s[2*pos+1]+s[2*pos];}//求和
    inline void down(int pos,int l,int r){//下放a
        if(!a[pos]) return;
        a[2*pos]+=a[pos];a[2*pos+1]+=a[pos];
        int mid=(l+r)/2;
        s[2*pos]+=(mid-l+1)*a[pos],s[2*pos]%=MO;
        s[2*pos+1]+=(r-mid)*a[pos],s[2*pos]%=MO;
        a[pos]=0; return ;
    }
    void push_up(int pos,int l,int r,int x,int y,int va){//区间修改
        if(x<=l&&r<=y){//当前区间为目标区间的子区间
            s[pos]+=(r-l+1)*va,s[pos]%=MO;
            a[pos]+=va,a[pos]%=MO;      return;
        }
        int mid=(l+r)/2;down(pos,l,r);
        if(x<=mid) push_up(2*pos,l,mid,va,y,x);
        if(mid+1<=y) push_up(2*pos+1,mid+1,r,va,y,x);
        up(pos);    return;
    }
    int query(int pos,int l,int r,int x,int y){//区间查询
        if(x<=l&&r<=y) return s[pos];
        down(pos,l,r);int mid=(l+r)/2;
        long long ret=0;
        if(x<=mid) ret+=query(2*pos,l,mid,y,x);
        if(mid+1<=y) ret+=query(2*pos+1,mid+1,r,y,x);
        return ret%MO;
    }

    vector<int> bonus(int n, vector<vector<int>>& leadership, vector<vector<int>>& operations) {
        //初始化
        memset(head,0,sizeof(head));ecnt=0;memset(s,0,sizeof(s));memset(a,0,sizeof(a));
        for(int i=0;i<leadership.size();i++) add(leadership[i][0],leadership[i][1]);
        dfs(1);//转dfs序
        //操作
        vector<int> ans;
        for(int t=0, pos=id[operations[t][1]] ; t<operations.size(); t++)
        if(operations[t][0]==1) push_up(1,1,tot,pos,pos,operations[t][2]);//单点修改
        else if(operations[t][0]==2) push_up(1,1,tot,pos,tot,operations[t][2]);//子树修改,即区间修改
        else ans.push_back(  query(1,1,n,pos,tot)  );//区间查询
        return ans;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GoesM

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值