【BZOJ3932】任务查询系统,主席树与差分的建树思想

Time:2016.05.08
Author:xiaoyimi
转载注明出处谢谢


传送门
思路:
说真的我一开始做这个题真不知道怎么建树,只知道应该以时间为根建树,记录优先值并维护前缀和,但想不出怎么用到主席树的前缀建树思想,暴力建树+修改肯定是不科学的……
后来去看题解,发现又是差分。
回想起被运输计划支配的恐惧……(还是自己太弱的缘故)
不过这个差分比较简单,因为它正好利用了第i棵主席树以第i-1棵树为基础建树的思路,比如某一任务的时间区间为[l,r],优先度为p,那么我们在root[l]上建树时+p,root[r+1]上建树时-p,这样的话就可以在枚举每个时间点的操作来建树了,因为有m个任务,每个任务有两个时间点的操作,所以最多建2m棵树,每个时间点所代表的主席树就是它最后一次修改所建的树,记录每个时间点最后被操作时所建的树的编号pos[i],查询操作就是空树与pos[i]相减得到了,这是主席树的基本操作,不过多赘述
注意:
1.简单计算可以得到这里的sum最多可以是100000*100000,超过了int,所以sum用long long计算
2.对于每个时间点的修改,有+有-,所以在原先的建树代码上稍作修改即可,记录系数什么的
代码:

#include<bits/stdc++.h>
#define M 100004
#define LL long long
#define ls(x) a[x].ch[0]
#define rs(x) a[x].ch[1] 
using namespace std;
int n,m,cnt,k;
int ID[M],S[M],E[M],P[M],pos[M];
LL lastans=1; 
vector <int> t[M];
struct disc
{
    int data,id;
    bool operator <(const disc other)const
    {
        return data<other.data;
    }
}b[M];
struct Chairman_tree
{
    int siz,ch[2];
    LL sum;
}a[M<<6];
int in()
{
    char ch=getchar();
    int t=0;
    while (!isdigit(ch)) ch=getchar();
    while (isdigit(ch)) t=(t<<3)+(t<<1)+ch-48,ch=getchar();
    return t;
}
void build(int now,int L,int R,int rt,int k,int val)
{
    a[rt].siz=a[now].siz+k;
    a[rt].sum=a[now].sum+k*ID[val];
    if (L==R) return;
    int mid=(L+R)>>1;
    if (mid>=val)
        rs(rt)=rs(now),
        ls(rt)=++cnt,
        build(ls(now),L,mid,ls(rt),k,val);
    else
        ls(rt)=ls(now),
        rs(rt)=++cnt,
        build(rs(now),mid+1,R,rs(rt),k,val);
}
LL get(int begin,int end,int L,int R,int k)
{
    if (begin==end) return a[R].sum-a[L].sum;
    int mid=(begin+end)>>1,t=a[ls(R)].siz-a[ls(L)].siz;
    if (k<=t)
        return get(begin,mid,ls(L),ls(R),k);
    else
        return a[ls(R)].sum-a[ls(L)].sum+get(mid+1,end,rs(L),rs(R),k-t);
}
main()
{
    m=in();n=in();
    for (int i=1;i<=m;i++)
        S[i]=in(),E[i]=in(),P[i]=in(),
        b[i].id=i,b[i].data=P[i];
    sort(b+1,b+m+1);
    for (int i=1;i<=m;i++)
        ID[i]=b[i].data,
        P[b[i].id]=i;
    cnt=1; 
    for (int i=1;i<=m;i++)
        t[S[i]].push_back(P[i]),
        t[E[i]+1].push_back(-P[i]), 
        cnt+=1+(E[i]+1<=n);
    for (int rt=1,i=1;i<=n;i++)
    {
        for (int j=0;j<t[i].size();j++)
            build(rt,1,n,rt+1,t[i][j]/abs(t[i][j]),abs(t[i][j])),
            rt++;
        pos[i]=rt;
    }
    int x;
    for (int i=1;i<=n;i++)
        x=in(),
        k=(lastans*in()+in())%in()+1,
        printf("%lld\n",lastans=get(1,n,1,pos[x],k));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值