正题
一说到区间修改,单点查询,你是不是想到了树状数组的差分。
那么既然有差分,我们就会开始思考怎么快速求前缀和。
你一开始可能会说,你给开始那个位置加上一个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<=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);//用一个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))); } }