题解 P5065 【[Ynoi2014]不归之人与望眼欲穿的人们】

出现了一篇跑得炒鸡慢的题解!

noteskey

无 fuck 说,好像就是整个数列分块然后合并区间...什么的吧

对于每块内部就是算一下前缀信息、后缀信息(就是以 第一个点/最后一个点 为一个边界,不超过 log 个不同的 or 值所要到达的最 左/右 点)和中值信息(就是某种区间长度内能 or 出来的最大值)

然后询问的时候从第一个块开始,向后先查询当前块与下一个块合并的答案,然后更新当前块

watch out

nothing ,打代码的时候注意细节貌似是所有 coding 的 gift ? 【雾

code

//by Judge
#pragma GCC optimize(3)
#pragma GCC optimize("unroll-loops")
#include<bits/stdc++.h>
#define Rg register
#define fp(i,a,b) for(Rg int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(Rg int i=(a),I=(b)-1;i>I;--i)
#define P pair<int,int>
#define ll long long
#define se second
#define fi first
using namespace std;
const int bl=403;
const int M=6e4+3;
typedef int arr[M];
#ifndef Judge
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
#endif
char buf[1<<21],*p1=buf,*p2=buf;
bool operator <(P& a,P& b){return a.fi^b.fi?a.fi<b.fi:a.se>b.se;}
inline bool cmax(int& a,int b){return a<b?a=b,1:0;}
inline bool cmin(int& a,int b){return a>b?a=b,1:0;}
inline int read(){ int x=0,f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f;
} char sr[1<<21],z[20];int CCF=-1,Z;
inline void Ot(){fwrite(sr,1,CCF+1,stdout),CCF=-1;}
inline void print(int x,char chr='\n'){
    if(CCF>1<<20)Ot();if(x<0)sr[++CCF]=45,x=-x;
    while(z[++Z]=x%10+48,x/=10);
    while(sr[++CCF]=z[Z],--Z);sr[++CCF]=chr;
} int n,m,K,ans,pv,a[M],pos[M];
P q[M],vec[bl+3],c[bl+3];
struct BLK{ int mx[bl+3],val[bl+3],L,R,cntp,cnts,all; P pre[33],suf[33]; //最多 log 个不同的 or 值 
    // mx[i]  长为 i 的区间 or 和的 max 值,val 表示块内每个点的值  
    inline void re(){ memset(mx,0,sizeof mx); int p; // 就算 mx 数组 
        p=cntp=0; fp(i,1,bl) if((p|=val[i])!=pre[cntp].fi) pre[++cntp]=P(p,i+L-1);
        p=cnts=0; fd(i,bl,1) if((p|=val[i])!=suf[cnts].fi) suf[++cnts]=P(p,i+L-1);
        int head=1,tail=0;
        fd(i,bl,1){ int lst=tail,v=val[i];
            for(Rg int j=v;j;j^=j&-j) q[++tail]=P(__builtin_ctz(j),i);
            fp(j,head,lst) if(!(v>>q[j].fi&1)) q[++tail]=q[j]; head=lst+1;
            Rg int now=0,pos; fp(j,head,tail)
                pos=q[j].se,cmax(mx[pos-i+1],now|=val[pos]);
        }
        fp(i,2,bl) cmax(mx[i],mx[i-1]); all=pre[cntp].fi;
    }
    inline void build(int l,int r){ L=l,R=r;
        fp(i,l,r) val[i-l+1]=a[i]; re();
    }
    inline void update(int pos,int v){val[pos-L+1]=v,re();} //重构块 
    inline int len(int v){return lower_bound(mx+1,mx+1+bl,v)-mx;}
    //得到构成长度为 v 的 or 和区间最短长度 
    inline void merge(){ int l=1,r=1,it=0; //将当前的区间和 vec 后缀合并 
        while(l<=pv&&r<=cnts) c[++it]=vec[l]<suf[r]?vec[l++]:suf[r++];
        while(l<=pv) c[++it]=vec[l++]; while(r<=cnts) c[++it]=suf[r++];
        vec[1]=c[1]; fp(i,2,it) vec[i]=c[i],
            vec[i].fi==vec[i-1].fi&&(vec[i].se=vec[i-1].se);
        pv=unique(vec+1,vec+1+it)-1-vec;
    }
    inline void check(int v){ int len=2e9; //用后缀 vec 和当前块的前缀更新答案 
        fp(i,1,cntp) while(pv&&(vec[pv].fi|pre[i].fi)>=v)
            cmin(len,pre[i].se-vec[pv--].se+1); cmin(ans,len);
    }
}b[131];
int main(){ n=read(),m=read(); fp(i,1,n) a[i]=read();
    fp(i,1,n) pos[i]=(i-1)/bl+1; K=pos[n],n=K*bl;
    fp(i,1,K) b[i].build((i-1)*bl+1,i*bl);
    while(m--){
        if(read()&1){ int x=read(),y=read();
            b[pos[x]].update(x,y),a[x]=y;
        } else{ ans=2e9; int x=read();
            fp(i,1,K) if(b[i].all>=x)
                cmin(ans,b[i].len(x));
            pv=0,b[1].merge();
            fp(i,2,K){
                b[i].check(x);
                fp(j,1,pv) vec[j].first|=b[i].all;
                b[i].merge();
            } print(ans<2e9?ans:-1);
        }
    } return Ot(),0;
}
上一篇:LOJ#2882. 「JOISC 2014 Day4」两个人的星座(计算几何)


下一篇:超容易理解的call()、apply()、bind()的区别