标签:stdin end cas 查询 getchar bcd 树节点 ++i ima
珂朵莉树模板题
怎么所有题解都是珂朵莉树啊啊啊啊
首先这棵线段树只维护懒标记
线段树节点\(u\)维护区间\([l_u,r_u]\)的内容
懒标记\(t_u\):当\(t_u\not=0\)时表示区间\([l_u,r_u]\)全是\(t_u\),\(t_u=0\)就是没有懒标记
在建立时顺便处理\(l_u,r_u\),只要当\(l_u=r_u\)时就打上标记
P.s \(Ls=2u,Rs=2u+1\)
void bld(int u){
if(t[u].l==t[u].r){t[u].v=rnd()%vmx+1;return;}
t[Ls].l=t[u].l,t[Rs].l=(t[Ls].r=(t[u].l+t[u].r)>>1)+1,t[Rs].r=t[u].r;
bld(Ls),bld(Rs);
}
找到所有被覆盖且有标记的区间,让\(t_u\)加上\(x\)
P.s Don‘t forget pushdown().
void pd(int u){if(t[u].v)t[Ls].v=t[Rs].v=t[u].v,t[u].v=0;}
void mdfa(int u){
if(r<t[u].l||t[u].r<l)return;
if(l<=t[u].l&&t[u].r<=r&&t[u].v)t[u].v+=x;
else pd(u),mdfa(Ls),mdfa(Rs);
}
与一般懒标记操作最为类似,找到覆盖区间,打上标记即可
void mdfs(int u){
if(r<t[u].l||t[u].r<l)return;
if(l<=t[u].l&&t[u].r<=r)t[u].v=x;
else pd(u),mdfs(Ls),mdfs(Rs);
}
在这里我们借助一个 vector
struct Q{int v,s;};
vector<Q>q;
其中\(v\)表示区间值,\(s\)表示区间长度
首先把所有有交集且有标记的区间全部存到这个 vector 里(注意\(s\)的处理)
void qry(int u){
if(r<t[u].l||t[u].r<l)return;
if(t[u].v)q.push_back({t[u].v,min(t[u].r,r)-max(t[u].l,l)+1});
else qry(Ls),qry(Rs);
}
那么对于区间第\(x\)小,将\(q\)按\(v\)排序,然后暴力即可 (vector 真好用)
q.clear(),qry(1);
sort(q.begin(),q.end(),[](Q a,Q b){return a.v<b.v;});
for(Q i:q){
if(x<=i.s){wr(i.v),Pe;break;}
x-=i.s;
}
对于区间幂次和,排序都不用了,直接暴扫 (这么感觉和珂朵莉树一副德行)
ans=0,y=rnd()%vmx+1,q.clear(),qry(1);
for(Q i:q)ans=(ans+i.s*fpw(i.v,x)%y)%y;
wr(ans),Pe;
Time complexity: \(O(\)玄学\()\)(大雾
Memory complexity: \(O(n)\)
附上总代码(\(10.60\)s / \(9.05\)MB)
(打的比珂朵莉树难,空间比珂朵莉树大,跑的比珂朵莉树慢)
//This program is written by Brian Peng.
#pragma GCC optimize("Ofast","inline","no-stack-protector")
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define Rd(a) (a=read())
#define Gc(a) (a=getchar())
#define Pc(a) putchar(a)
int read(){
int u;char c(getchar());bool k;
while(!isdigit(c)&&c^'-')if(Gc(c)==EOF)exit(0);
if(c^'-')k=1,u=c&15;else k=u=0;
while(isdigit(Gc(c)))u=(u<<1)+(u<<3)+(c&15);
return k?u:-u;
}
void wr(int a){
if(a<0)Pc('-'),a=-a;
if(a<=9)Pc(a|'0');
else wr(a/10),Pc((a%10)|'0');
}
signed const INF(0x3f3f3f3f),NINF(0xc3c3c3c3);
long long const LINF(0x3f3f3f3f3f3f3f3fLL),LNINF(0xc3c3c3c3c3c3c3c3LL);
#define Ps Pc(' ')
#define Pe Pc('\n')
#define Frn0(i,a,b) for(int i(a);i<(b);++i)
#define Frn1(i,a,b) for(int i(a);i<=(b);++i)
#define Frn_(i,a,b) for(int i(a);i>=(b);--i)
#define Mst(a,b) memset(a,b,sizeof(a))
#define File(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout)
#define N (262150)
#define Ls (u<<1)
#define Rs (Ls|1)
int n,m,sd,vmx,op,l,r,x,y,ans;
struct SgT{int l,r,v;}t[N];
struct Q{int v,s;};
vector<Q>q;
int rnd(){int r(sd);sd=(sd*7+13)%1000000007;return r;}
void pd(int u){if(t[u].v)t[Ls].v=t[Rs].v=t[u].v,t[u].v=0;}
void bld(int u);
void mdfa(int u);
void mdfs(int u);
void qry(int u);
int fpw(int a,int b){int r(1);a%=y;while(b)b&1?r=r*a%y:0,a=a*a%y,b>>=1;return r;}
signed main(){
t[1].l=1,t[1].r=Rd(n),Rd(m),Rd(sd),Rd(vmx);
bld(1);
while(m--){
op=rnd()%4+1,l=rnd()%n+1,r=rnd()%n+1;
if(l>r)swap(l,r);
x=rnd()%(op==3?r-l+1:vmx)+1;
switch(op){
case 1:mdfa(1);break;
case 2:mdfs(1);break;
case 3:q.clear(),qry(1);
sort(q.begin(),q.end(),[](Q a,Q b){return a.v<b.v;});
for(Q i:q){
if(x<=i.s){wr(i.v),Pe;break;}
x-=i.s;
}break;
case 4:ans=0,y=rnd()%vmx+1,q.clear(),qry(1);
for(Q i:q)ans=(ans+i.s*fpw(i.v,x)%y)%y;
wr(ans),Pe;break;
}
}
exit(0);
}
void bld(int u){
if(t[u].l==t[u].r){t[u].v=rnd()%vmx+1;return;}
t[Ls].l=t[u].l,t[Rs].l=(t[Ls].r=(t[u].l+t[u].r)>>1)+1,t[Rs].r=t[u].r;
bld(Ls),bld(Rs);
}
void mdfa(int u){
if(r<t[u].l||t[u].r<l)return;
if(l<=t[u].l&&t[u].r<=r&&t[u].v)t[u].v+=x;
else pd(u),mdfa(Ls),mdfa(Rs);
}
void mdfs(int u){
if(r<t[u].l||t[u].r<l)return;
if(l<=t[u].l&&t[u].r<=r)t[u].v=x;
else pd(u),mdfs(Ls),mdfs(Rs);
}
void qry(int u){
if(r<t[u].l||t[u].r<l)return;
if(t[u].v)q.push_back({t[u].v,min(t[u].r,r)-max(t[u].l,l)+1});
else qry(Ls),qry(Rs);
}
仔细想想其实这个线段树也是利用随机数据的多次区间设置,减少有效懒标记的数量以优化查询复杂度
如果没有区间设置,照样\(O(nm)\)爆炸
所以以后看见线段树是这么构造的,就果断使用珂朵莉树吧(大雾
虽然这题我没打珂朵莉树,但是
有输入法为证
Solution: 题解 CF896C Willem, Chtholly and Seniorious(线段树解珂朵莉树)
标签:stdin end cas 查询 getchar bcd 树节点 ++i ima
原文地址:https://www.cnblogs.com/BrianPeng/p/12356392.html