LOJ#2461. 「2018 集训队互测 Day 1」完美的队列

可以先看一下这篇,写的比较详细了

我们考虑对每个询问 j j 求出一个 ed[j] e d [ j ] ,表示在执行完 (j,ed[j]] ( j , e d [ j ] ] 的操作后, j j 在序列里加入的所有 x x 全部被pop出去了,就可以对每个颜色 x x 求出若干个存在的区间,将这些区间取并,即可差分贡献到答案

现在考虑怎么求 ed[j] e d [ j ]
我们将原序列分块,操作 j j 覆盖了若干个整块和0/1/2个散块,我们分开考虑整块和散块对 ed[j] e d [ j ] 的贡献
设操作 j j 加入了元素 xj x j

对于整块,我们枚举每个块,维护两个指针 j,k j , k ,表示当前考虑的操作是 (j,k] ( j , k ]
b[i] b [ i ] 表示位置 i i 再被push b[i] b [ i ] 次后就会将 xj x j pop掉
mx=max(b[i]) m x = m a x ( b [ i ] )
cov表示这个块被完整覆盖了cov次
mx-cov<=0时就代表这个块内的所有 xj x j 都被pop出去了,此时 k k 就可以对 ed[j] e d [ j ] 产生贡献
考虑怎么维护,新加入一个操作时,若他完整覆盖了这个块, cov c o v ++,否则若他和这个块有交,交集部分的 b[i] b [ i ] - -,然后直接扫一遍整个块更新 mx m x ,撤销操作类似
每个操作贡献至多 2n 2 n ,这部分的复杂度是 mn m n

对于散块,我们需要在处理整块的时候先处理一些东西:
s[i] s [ i ] 表示操作1~ i i 完整覆盖了当前块 s[i] s [ i ]
c[i] c [ i ] 表示第 i i 个完整覆盖当前块的操作
d[i] d [ i ] 表示第 i i 个不完整覆盖当前块,但和当前块有交的操作
e[i] e [ i ] 表示 s[j]=i s [ j ] = i j j 里面最小的 j j

然后我们枚举整块里的每个位置 i i ,考虑他作为被散块覆盖的贡献
d[] d [ ] 上维护双指针 j,k j , k ,表示当前考虑的操作是 (d[j],d[k] ] ( d [ j ] , d [ k ]   ] ,维护一个 mx m x 表示位置 i i 当前被覆盖的次数
对于每个 j j k k 不断往后跳直到 mx<=0 m x <= 0 ,mx可以用前缀和及操作 d[j],d[k] d [ j ] , d [ k ] 是否覆盖 i i 直接更新,然后根据mx是否=0,操作 d[k] d [ k ] 是否覆盖 i i ,是可以用上面处理的几个数组 O(1) O ( 1 ) 找到位置 i i xj x j 被pop出去的时刻的
这部分的复杂度只跟每个块的 d[] d [ ] 的大小有关,每个操作对贡献 O(1) O ( 1 ) d[] d [ ] ,所以这部分的复杂度也是 mn m n

所以总复杂度是 O(mn) O ( m n )

code:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define pb push_back
#define mp make_pair
#define SZ(x) (int)x.size()
#define fir first
#define sec second
using namespace std;

inline void read(int &x)
{
    char c; while(!((c=getchar())>='0'&&c<='9'));
    x=c-'0';
    while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
}
inline void up(int &a,const int &b){if(aconst int maxn = 210000;

int n,m,N;
int a[maxn],b[maxn];
int l[maxn],r[maxn],X[maxn],ed[maxn];

int s[maxn];
int c[maxn],cn,d[maxn],dn,e[maxn];

vector< pair<int,int> >V[maxn];
int f[maxn];

int main()
{
    //freopen("tmp.in","r",stdin);
    //freopen("tmp.out","w",stdout);

    read(n); read(m); N=sqrt(n);
    for(int i=1;i<=n;i++) read(a[i]);
    for(int i=1;i<=m;i++) read(l[i]),read(r[i]),read(X[i]);

    for(int L=1;L<=n;L+=N)
    {
        int R=min(L+N-1,n);
        int mx=0; for(int i=L;i<=R;i++) up(mx,b[i]=a[i]);
        cn=dn=0;
        for(int j=1,k=0,cov=0;j<=m;j++)
        {
            if(l[j]<=L&&r[j]>=R) cov--;
            else if(l[j]<=R&&r[j]>=L)
            {
                for(int i=max(l[j],L),ui=min(r[j],R);i<=ui;i++) b[i]++;
                mx=0; for(int i=L;i<=R;i++) up(mx,b[i]);
            }
            while(k<=m&&mx-cov>0)
            {
                ++k;
                if(l[k]<=L&&r[k]>=R) cov++;
                else if(l[k]<=R&&r[k]>=L)
                {
                    for(int i=max(l[k],L),ui=min(r[k],R);i<=ui;i++) b[i]--;
                    mx=0; for(int i=L;i<=R;i++) up(mx,b[i]);
                }
            }

            s[j]=s[j-1];
            if(l[j]<=L&&r[j]>=R) up(ed[j],k),s[c[++cn]=j]++;
            else if(l[j]<=R&&r[j]>=L) d[++dn]=j,e[dn]=cn;
        }
        for(int i=L;i<=R;i++)
        {
            mx=a[i];
            for(int j=1,k=0;j<=dn;j++)
            {
                mx+=s[d[j]]-s[d[j-1]];
                if(l[d[j]]<=i&&r[d[j]]>=i)
                {
                    mx++;
                    while(k0) ++k,mx-=s[d[k]]-s[d[k-1]]+(l[d[k]]<=i&&r[d[k]]>=i);
                    if(mx>0)
                    {
                        if(mx>s[m]-s[d[k]]) ed[d[j]]=m+1;
                        else up(ed[d[j]],c[e[k]+mx]);
                        continue;
                    }

                    if(l[d[k]]<=i&&r[d[k]]>=i) up(ed[d[j]],mx<0?c[e[k]+mx+1]:d[k]);
                    else up(ed[d[j]],c[e[k]+mx]);
                }
            }
        }
    }

    //for(int i=1;i<=m;i++) printf("%d\n",ed[i]);

    for(int i=1;i<=m;i++) V[X[i]].pb(mp(i,1)),V[X[i]].pb(mp(ed[i],-1));
    for(int i=1;i<=100000;i++) 
    {
        sort(V[i].begin(),V[i].end());
        int now=0;
        for(int j=0;jint lnow=now;
            now+=V[i][j].sec;
            for(int k=j+1;kif(lnow==0&&now>0) f[V[i][j].fir]++;
            else if(lnow>0&&now==0) f[V[i][j].fir]--;
        }
    }

    for(int i=1,ans=0;i<=m;i++) printf("%d\n",(ans+=f[i]));

    return 0;
}

你可能感兴趣的:(分块大法好,LOJ)