标签:break 实现 problem 出现 home 不同的 数字 getchar 直接
什么是瑟尼欧里斯树?(大雾)
口胡珂学数据结构?
瑟尼欧里斯树是为了纪念珂朵莉而诞生的,因为有了珂朵莉树,所以只能叫瑟尼欧里斯树(大雾)
瑟尼欧里斯树主要资瓷以下两种操作:
1.把区间【l,r】里大于/小于(等于)x的数减去/加上x
2.求区间【l,r】里有多少数等于x
No1.CF896E Welcome home, Chtholly
链接:https://www.luogu.org/problemnew/show/CF896E
做法简介:板子题
No2.YNOI2018 五彩斑斓的世界
链接:https://www.luogu.org/problemnew/show/P4117
做法简介:板子+卡常
瑟尼欧里斯树实际就是分块,只是块的形式和普通的分块不同。
维护时,每个块内,维护这这个块中每一种不同的数字的首地址,其他的数用并查集连到首地址中,减的操作就可以直接用lazy_tag打到首地址上。(边块直接暴力,一个一个扫描)
查询时,在所对应的块中扫描首地址暴力就行了。(边块时更暴力,一个一个扫描)
瑟尼欧里斯树和珂朵莉树很像,都是把相同的数存在一起,只是维护的方式稍有不同。
注解足够,没什么好讲的。
#pragma GCC optimize("-O3") //手开O3
#include<bits/stdc++.h>
#define sight(c) (‘0‘<=c&&c<=‘9‘)
#define SIZ 254
#define OTK 407
#define Re register
#define N 100607
#define fp puts
#define l(x) (x*SIZ-SIZ+1)
#define r(x) (x*SIZ)
inline void read(int &x){
static char c;
for (c=getchar();!sight(c);c=getchar());
for (x=0;sight(c);c=getchar())x=x*10+c-48;
}
void write(int x){
if (x<10) {putchar(‘0‘+x); return;}
write(x/10);
putchar(‘0‘+x%10);
}
inline void writeln(int x){
if (x<0) putchar(‘-‘),x*=-1;
write(x);
putchar(‘\n‘);
}
using namespace std;
struct RR{
int num,root;
}V[OTK][N];
int pre[N],pos[N],a[N],ma[N],n,m,be[N],op,l,r,v,p,q,ans,lz[N];
inline int fa(int x){
//找节点的首地址
while (x^pre[x])
x=pre[x]=pre[pre[x]];
return x;
}
inline void push(int x) {
//取消懒标记
for (Re int i=l(x);i<=r(x);i++)
{
a[i]=pos[fa(i)];
V[x][a[i]].root=V[x][a[i]].num=0;
a[i]-=lz[x];
}
for (int i=l(x);i<=r(x);i++)
pre[i]=0;
lz[x]=0;
}
inline void reb(int x){
ma[x]=0;
for (Re int i=l(x);i<=r(x);i++) //l(x),r(x)表示块的左右
{
if (a[i]>ma[x])
ma[x]=a[i]; //找最大
V[x][a[i]].root?pre[i]=V[x][a[i]].root:(pos[i]=a[i],V[x][a[i]].root=i,pre[i]=i);
//判定是否有祖先(首地址),如果有,就并入首地址的数组(并查集),如果没有,就新开一个首地址
V[x][a[i]].num++; //首地址表示相同的数的数量加上新进来的
}
}
RR *A,*B;
void play(int x,int a,int b){
A=&V[x][a];
B=&V[x][b];
B->root?pre[A->root]=B->root:(B->root=A->root,pos[A->root]=b);
B->num+=A->num;
A->num=A->root=0;
}
inline void det(int x,int v){
//加懒标记
int &p=lz[x] ,&q=ma[x];
if ((v<<1)<=q-p)
{
for (Re int i=p+1;i<=p+v;i++)
if (V[x][i].root)
play(x,i,i+v);
p+=v;
}
else
{
for (Re int i=q;i>p+v;i--)
if (V[x][i].root)
play(x,i,i-v);
q=min(q,p+v);
}
}
int main () {
//读入
read(n);
read(m);
for (Re int i=1;i<=n;i++)
{
read(a[i]);
be[i]=(i-1)/SIZ+1; //标记每个点分别属于哪一个块
}
for (Re int i=1;i<=be[n];i++)
reb(i); //初始化
while (m--) {
//读入
read(op);
read(l);
read(r);
read(v);
switch (op) {
case 1: //维护操作
p=be[l]; //l所在的块(区间第一块)
q=be[r]; //r所在的块( 区间最后一块)
if (p^q) //如果p不等于q(区间包含多个块)
{
push(p); //把左右两个暴力做的边块的lazy_tag给push_down(把懒标记暴力去除)
push(q);
for (Re int i=l;i<=r(p);i++) //最左边的块暴力一个一个减
if (a[i]>v)
a[i]-=v;
for (Re int i=l(q);i<=r;i++) //最右边的块暴力一个一个减
if (a[i]>v)
a[i]-=v;
for (Re int i=p+1;i<q;i++) //中间的直接打到lazy_tag上
det(i,v);
reb(p); //更新左右块内的情况
reb(q);
}
else {
push(p); //暴力把块的lazy_tag给push_down(把懒标记暴力去除)
for (Re int i=l;i<=r;i++) //暴力减
if (a[i]>v)
a[i]-=v;
reb(p); //更新情况
}
break;
case 2: //查询操作
p=be[l]; //l所在的块(区间第一块)
q=be[r]; //r所在的块( 区间最后一块)
ans=0;
if (p^q) //如果p不等于q(区间包含多个块)
{
for (Re int i=l;i<=r(p);i++) //最左边的块暴力一个一个加进答案
if (pos[fa(i)]-lz[p]==v)
ans++;
for (Re int i=l(q);i<=r;i++) //最右边的块暴力一个一个加进答案
if (pos[fa(i)]-lz[q]==v)
ans++;
for (Re int i=p+1;i<q;i++) //中间的块利用首地址加进答案
if (v+lz[i]<N)
ans+=V[i][v+lz[i]].num;
}
else
for (Re int i=l;i<=r;i++) //这段的内容暴力加入答案
if (pos[fa(i)]-lz[p]==v)
ans++;
writeln(ans); //输出
break;
}
}
return 0;
}
实际珂朵莉式卡常也可以(大雾)
标签:break 实现 problem 出现 home 不同的 数字 getchar 直接
原文地址:https://www.cnblogs.com/yzhang-rp-inf/p/9451738.html