正题
一说到区间修改,单点查询,你是不是想到了树状数组的差分。
那么既然有差分,我们就会开始思考怎么快速求前缀和。
你一开始可能会说,你给开始那个位置加上一个k,结束的后一个位置减去一个k,然后用树状数组套主席树来维护一下不就行了emm。
然后你知道你的复杂度是nloglogn,你超时了哈哈哈哈~
其实中途不带修改,我们就可以直接把,当前点的信息,通过前缀和统计算出来就可以了。
for(int i=1;i<=n;i++){
int x,y,k;
scanf("%d %d %d",&x,&y,&k);
ins(x,k,1);ins(y+1,k,-1);//相当于一个建边的过程
}
int L=0;
for(int i=1;i<=m;i++){
for(int j=first[i];j!=0;j=s[j].next){//邻接表来构建,遍历在这时有改变的状态
d=s[j].d,v=s[j].x;
L++;
update(root[L-1],root[L],1,10000000);//用一个L虚拟根来模拟每次修改,用spj来记录i点真正的根
}
spj[i]=L;
}建出来前缀树之后,我们就可以对于每个询问找对应的树就可以了,时间复杂度控制在nlogn.
代码<主要是函数写得巧妙一些,spj的概念>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
struct edge{
int x,d,next;
}s[300010];
int first[100010];
int n,m;
int len=0;
int ls[5000010],rs[5000010];
int c[5000010];
long long t[5000010];
int d,v;
int root[5000010];
int spj[100010];
int tot=0;
void ins(int x,int y,int c){
len++;
s[len].x=y;s[len].d=c;s[len].next=first[x];first[x]=len;
}
void update(int y,int&x,int l,int r){
ls[x=++tot]=ls[y];rs[x]=rs[y];c[x]=c[y];t[x]=t[y];
c[x]+=d;t[x]+=v*d;
if(l==r) return ;
if(v<=(l+r)/2) update(ls[y],ls[x],l,(l+r)/2);
else update(rs[y],rs[x],(l+r)/2+1,r);
}
long long solve(int x,long long k,int l,int r){
if(l==r) return k*l;
if(k<=c[ls[x]]) return solve(ls[x],k,l,(l+r)/2);
else return solve(rs[x],k-c[ls[x]],(l+r)/2+1,r)+t[ls[x]];
}
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++){
int x,y,k;
scanf("%d %d %d",&x,&y,&k);
ins(x,k,1);ins(y+1,k,-1);
}
int L=0;
for(int i=1;i<=n;i++){
for(int j=first[i];j!=0;j=s[j].next){
d=s[j].d,v=s[j].x;
L++;
update(root[L-1],root[L],1,10000000);
}
spj[i]=L;
}
int x;
long long last=1;
long long a,b,op;
long long k;
int rt=0;
for(int i=1;i<=m;i++){
scanf("%d %lld %lld %lld",&x,&a,&b,&op);
k=1+((a%op*last%op)+b%op)%op;rt=root[spj[x]];
printf("%lld\n",last=(c[rt]<=k?t[rt]:solve(rt,k,1,10000000)));
}
}
1606

被折叠的 条评论
为什么被折叠?



