[BZOJ5145] [Ynoi2018] 五彩斑斓的世界 [并查集][分块][摊还分析]

题目链接
题意:维护一个序列。
有两种操作,一种叫做把[l,r]的大于x的数减掉x,
一种是查询[l,r]里面x的出现次数。

真红世界第一可爱(大声

简单粗暴的opt题目
好像整个序列里面的数再怎么变最小也就是0,而且变成0就不会动了
感觉可以分析一波复杂度不过还没有思路好像太早了点
在开始找思路之前先定义一个 U = 1 0 5 U=10^5 (本题数据上界)

查询怎么做?
主席树懒修改的话查询又一定要推到底,那可能要用某种奇怪的姿势搞分块暴力
一个块里面相同的那些数可以做成一个集合,修改其实就是把集合 i i 转移进 i x i-x
(当然不能只转一个 c n t cnt ,不然待会暴力重构边缘块的时候就凉凉了)
这样暴力的,复杂度好像有点高吧?
那怎么做马?作为一个合格的能过这道题的分块至少也要 Θ ( U U ) \Theta(U\sqrt{U})

这个复杂度真吗?
第一个操作是什么?把 [ l , r ] [l,r] 里面大于 x x 的减掉 x x
这两个 x x 有没有很玄,想想是不是能够摊还

M x [ i ] Mx[i] 代表块 i i 的最大值,块的初势就是 M x [ i ] U Mx[i]\le U
N \sqrt{N} 个块, 想想能不能做到 U N U\sqrt{N} ——也就是付出一点的代价至少让 M x Mx 减少一点
修改的时候吧,对于 [ l , r ] [l,r] 里面的块,
如果 M x [ i ] x Mx[i]\le x 就跳过。
M x [ i ] > x Mx[i]> x ,可以消耗 M x [ i ] x Mx[i]-x 次的费用使 M x [ i ] Mx[i] 至少减少 m i n ( x , M x [ i ] x ) min(x,Mx[i]-x)
这个在 M x [ i ] 2 x Mx[i]\le 2x 的情况下代价等于势能减少量,很阔以

可是 2 x < M x [ i ] 2x < Mx[i] 的情况下代价大于势能减少量,这就很不阔以了。
既然 M x Mx 减少的量在这种情况下固定为 x x 了,那代价怎么说也得 x \le x 吧?
实际上把 > x > x 的下移相当于把 x \le x 的上移 x x 然后整体 x -x
于是修改的时候把 x \le x 的拿来加 x x ,给块加一个整体 x -x 的标记。

总的复杂度 Θ ( ( U + M ) N ) \Theta((U+M)\sqrt{N})

ps.洛谷上面Input写错了,第二个操作应该是2 l r x,x写成了p(

真实的卡常数大赛
数据范围弱化

听说空间可以压下40M
不过我就不专门开个数据类型了(

Accepted 128076kb
4822B    15396ms
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#define reg register int
#define max(a,b) a>b?a:b
#define min(a,b) a<b?a:b
#define getchar() (frS==frT&&(frT=(frS=frBB)+fread(frBB,1,1<<12,stdin),frS==frT)?EOF:*frS++)
using namespace std;
char frBB[1<<12],*frS=frBB,*frT=frBB;
inline int read()
{
    int x=0;char ch=0;
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))x=x*10+ch-48,ch=getchar();
    return x;
}
template<typename T>
inline void write(T x)
{
    if(x>9)write(x/10);
    putchar(x%10+48);
}
int N,M,siz,cnt,t;
int Ai[100005]={};
int lb[320]={},rb[320]={};
short belong[100005]={};
int root[320][100005]={};
int sub[320]={};
int mx[320]={};
int fa[100005]={};
int roottype[100005]={};
int rootcnt[100005]={};
inline int find(const int&x){return (fa[x]==x)?x:(fa[x]=find(fa[x]));}
inline void merge(const int&id,const int&a,const int&b)
{
    if(!root[id][b])
    {
        root[id][b]=root[id][a];
        roottype[root[id][a]]=b;
    }
    else
    {
        fa[root[id][a]]=root[id][b];
        rootcnt[root[id][b]]+=rootcnt[root[id][a]];
    }
    root[id][a]=0;
}
inline void preload(const reg&id)
{
    mx[id]=0;
    for(reg i=lb[id];i<=rb[id];++i)
    {
        mx[id]=max(mx[id],Ai[i]);
        rootcnt[i]=1;
        if(!root[id][Ai[i]])
        {
            root[id][Ai[i]]=i;
            fa[i]=i;
            roottype[i]=Ai[i];
        }
        else
        {
            fa[i]=root[id][Ai[i]];
            rootcnt[fa[i]]+=rootcnt[i];
        }
    }
}
inline void _clear(const reg&id)
{
    for(reg i=lb[id];i<=rb[id];++i)
    {
        Ai[i]=roottype[find(i)];
        root[id][Ai[i]]=0;
        Ai[i]-=sub[id];
    }
    for(reg i=lb[id];i<=rb[id];++i)fa[i]=0;
    sub[id]=0;
}
inline void work(const reg&id,const reg&x)
{
    if(mx[id]-sub[id]<=x)return;
    if(mx[id]-sub[id]<2*x)
    {
        for(reg i=sub[id]+x+1;i<=mx[id];++i)if(root[id][i])merge(id,i,i-x);
        mx[id]=min(mx[id],x+sub[id]);
    }
    else
    {
        for(reg i=sub[id]+1;i<=sub[id]+x;++i)if(root[id][i])merge(id,i,i+x);
        sub[id]+=x;
    }
}
inline void modify(const reg&l,const reg&r,const reg&x)
{
    reg lblock=belong[l],rblock=belong[r];
    if(lblock==rblock)
    {
        _clear(lblock);
        for(reg i=l;i<=r;++i)if(Ai[i]>x)Ai[i]-=x;
        preload(lblock);
    }
    else
    {
        _clear(lblock); _clear(rblock);
        for(reg i=l;i<=rb[lblock];++i)if(Ai[i]>x)Ai[i]-=x;
        for(reg i=lblock+1;i<rblock;++i)work(i,x);
        for(reg i=lb[rblock];i<=r;++i)if(Ai[i]>x)Ai[i]-=x;
        preload(lblock); preload(rblock);
    }
}
inline int query(const reg&l,const reg&r,const reg&x)
{
    reg lblock=belong[l],rblock=belong[r],ret=0;
    if(lblock!=rblock)
    {
		t=x+sub[lblock];
        for(reg i=l;i<=rb[lblock];++i)if(roottype[find(i)]==t)++ret;
        t=x+sub[rblock];
        for(reg i=lb[rblock];i<=r;++i)if(roottype[find(i)]==t)++ret;
        for(reg i=lblock+1;i<rblock;++i)if(x<=mx[i]-sub[i])ret+=rootcnt[root[i][x+sub[i]]];
    }
    else
    {
        for(reg i=l;i<=r;++i)
        {
            if(roottype[find(i)]==x+sub[lblock])++ret;
        }
    }
    return ret;
}
#define read() read()
#define write(x) write(x)
#define find(x) find(x)
#define merge(id,a,b) merge(id,a,b)
#define preload(id) preload(id)
#define _clear(id) _clear(id)
#define work(id,x) work(id,x)
#define modify(l,r,x) modify(l,r,x)
int main()
{
    N=read(); M=read(); siz=sqrt(N); cnt=(N-1)/siz+1;
    for(reg i=1;i<=N;++i)Ai[i]=read();
    for(reg i=1;i<=cnt;++i)
    {
        lb[i]=(i-1)*siz+1;
        rb[i]=i*siz;
        if(i==cnt)rb[i]=N;
        for(reg j=lb[i];j<=rb[i];++j)
        {
            rootcnt[j]=1;
            belong[j]=i;;
            mx[i]=max(mx[i],Ai[j]);
            if(!root[i][Ai[j]])
            {
                fa[j]=j;
                root[i][Ai[j]]=j;
                roottype[j]=Ai[j];
            }
            else
            {
                fa[j]=root[i][Ai[j]];
                rootcnt[fa[j]]+=rootcnt[j];
            }
        }
    }
    reg opt,l,r,x;
    while(M--)
    {
        opt=read(); l=read(); r=read(); x=read();
        if(opt==1)modify(l,r,x);
        if(opt==2)write(query(l,r,x)),putchar('\n');
    }
    return 0;
}

F社咕咕咕
等两个月不知道能不能看见新作

猜你喜欢

转载自blog.csdn.net/Estia_/article/details/83050960
今日推荐