• 【考试总结】20220329


    交通

    倒序预处理 到达每个路口的时间为绿灯第 \(0\) 秒时到终点的最短用时

    转移就是找到后缀第一个区间和在模意义下属于 \([g,g+r)\) 的位置,这个部分可以使用动态开点线段树维护

    查询和预处理的需求形式类似,不过查询时直接暴力跑过去了是怎么回事呢

    Code Display
    const int N=5e4+10;
    int f[N],n,g,r,d[N],Q,cyc,s[N];
    const int M=N*90;
    struct Seg{
        int Mn[M],ls[M],rs[M],tot,rt;
        inline void upd(int pos,int v,int &p,int l=0,int r=g+r-1){
            if(!p) p=++tot;
            if(l==r) return Mn[p]=v,void(); 
            int mid=(l+r)>>1;
            if(pos<=mid) upd(pos,v,ls[p],l,mid);
            else upd(pos,v,rs[p],mid+1,r);
            Mn[p]=n+2;
            if(ls[p]) ckmin(Mn[p],Mn[ls[p]]);
            if(rs[p]) ckmin(Mn[p],Mn[rs[p]]);
            return ;
        }
        inline int query(int st,int ed,int p,int l=0,int r=g+r-1){
            if(!p) return n+2;
            if(st<=l&&r<=ed) return Mn[p];
            int mid=(l+r)>>1,res=n+2;
            if(st<=mid) ckmin(res,query(st,ed,ls[p],l,mid));
            if(ed>mid) ckmin(res,query(st,ed,rs[p],mid+1,r));
            return res;
        }
    }T;
    inline int ask(int L,int R){
        L%=(g+r); R%=(g+r);
        if(L<=R) return T.query(L,R,T.rt);
        return min(T.query(L,g+r-1,T.rt),T.query(0,R,T.rt));
    }
    signed main(){
        freopen("traffic.in","r",stdin); freopen("traffic.out","w",stdout);
        n=read(); g=read(); r=read();
        rep(i,1,n+1) d[i]=read(),cyc+=d[i]/(g+r),d[i]%=g+r;
        
        s[1]=d[1];
        for(int i=2;i<=n+1;++i) s[i]=d[i]+s[i-1];
        
        f[n+1]=d[n+1];
        T.upd(s[n]%(g+r),n+1,T.rt);
        
        for(int i=n;i>=1;--i){
            int j=ask(s[i-1]+g,s[i-1]+g+r-1);
            if(j!=n+2){
                int v=s[j-1]-s[i-1];
                f[i]=v+f[j]+(g+r)-v%(g+r);
            }else f[i]=s[n+1]-s[i-1];
            T.upd(s[i-1]%(g+r),i,T.rt);
        }
        int Q=read();
        while(Q--){
            int t=d[1]+read();
            for(int i=2;i<=n+1;++i){
                if(t%(g+r)>=g){
                    t+=(g+r)-t%(g+r);
                    t+=f[i];
                    break;
                }
                t+=d[i];
            }
            print(t+cyc*(g+r)); 
        }
        return 0;
    }
    

    选拔

    诈 骗 大 师

    考虑对于每个询问分开处理:设 \(f_{x,j}\) 表示从 \(x\) 子树里面是否存在一个点的根链满足对应字符串是 \(Q[1..i]\)\(g_{x,j}\) 表示匹配后缀

    转移显然可以使用 std::bitset 优化

    对于所有询问可以统一处理,将字符串连到一起并添加特殊字符即可

    一种可能的实现是记录每个字符在全部字符串中的出现位置,并将特殊字符设为 \('z'+1\),每个树上节点初始化 \(\rm bitset\) 时置为特殊字符对应的出现位置

    同时全局维护一个长度为拼接串串长的 \(\rm bitset\),其对应字符串上字符的位置表示用上述前后缀的 \(\rm DP\) 值是否可以 \(\rm and\) 得到

    Code Display
    const int N=3e4+10;
    bitset<N*2> f[N],g[N],ans,b[27];
    string s,t[N];
    int n,Q;
    vector<pair<int,int> > G[N];
    int tim,ord[N],fa[N];
    inline void get_fa(int x,int fat){
        ord[++tim]=x; fa[x]=fat;
        f[x]=g[x]=b[26];
        for(auto t:G[x]) if(t.fir!=fat) get_fa(t.fir,x);
        return ;
    }
    signed main(){
        freopen("selection.in","r",stdin); freopen("selection.out","w",stdout);
        n=read();
        for(int i=1;i<n;++i){
            int u=read(),v=read(),k=Getalpha()-'a';
            G[u].emplace_back(v,k);
            G[v].emplace_back(u,k);
        }
        Q=read();
        for(int i=1;i<=Q;++i){
            cin>>t[i];
            s=s+char('z'+1)+t[i];
        }
        s+=char('z'+1);
        int len=s.length();
        for(int i=0;i<len;++i) b[s[i]-'a'][i]=1;
        get_fa(1,0);
        for(int id=n;id>=1;--id){
            int x=ord[id];
            for(auto e:G[x]){
                int t=e.fir; if(t==fa[x]) continue;
                f[t]=(f[t]<<1)&b[e.sec];
                g[t]=(g[t]>>1)&b[e.sec];
                ans=ans|((f[x]<<1)&g[t]);
                ans=ans|(g[x]&(f[t]<<1));
                f[x]|=f[t]; g[x]|=g[t];
            }
        }
        int cur=1;
        for(int i=1;i<=Q;++i){
            int tlen=t[i].size();
            for(int j=0;j<=tlen;++j) if(ans[cur+j]){puts("YES"); goto Succ;}
            puts("NO");
            Succ:;
            cur+=tlen+1;
        }
        return 0;
    }
    

    等待

    尝试判断某个数字 \(x\) 是否能作为合法的 \(\sum b\),简记 \(a_1\ge a_2\dots\ge a_n\)

    • 如果 \(x\) 的最高位比 \(a_{\max}\) 的最高位高那么删掉 \(a_{\max}\) 变成子问题

    • 如果 \(x\) 的最高位和 \(a_{\max}\) 的最高位相同那么删掉 \(a_{\max}\) 的最高位得到新的 \(\{a\}\) 变成子问题

    • 如果 \(x\) 的最高位比 \(a_{\max}\) 的最高位低那么不合法

    不难发现这个做法没有后效性

    显然可以在判定低位时保留高位对 \(a\) 的删除作用

    具体实现可以使用 \(\text{radix sort}\) 来找到删掉若干个 \(1\) 之后的 \(a_{\max}\),基数排序过程就是维护前 \(i-1\) 位的排序结果并添加第 \(i\) 位,这位不同的直接由这位来决定大小关系

    根据需求:对于最高位 \(<i\) 的数字不在处理第 \(i\) 位时考虑

    这部分复杂度是 \(\Theta(\sum L)\) 的,懒得对 \(0/1\) 归并可以直接 std::sort,问题不大

    发现有些部分可以合并处理,也就是有一段 \(\sum b\) 上的连续位必须同时填 \(1\) ,考虑如下局面:

    现在考虑到 \(\sum b\) 的第 \(p\) 位,仍然设 \(a_1\ge a_2\dots\ge a_n\),同时记 \(t_i\) 表示 \(a_i\) 的最高位,记 \(k=\min\{\arg \max\{i+t_i\}\}\)

    如果 \(p>k+t_k\) 那么一定有解,因为每个数字都要比其最高位更高的 \(1\) 抵消之,同理若 \(p<k+t_k\) 那么一定没有解,据此可以加速判断

    但是仍然有一个 \(p=k+t_k\)\(\text{case}\) 需要处理,那么 \(p\leftarrow p-k,\) 并将 \(a_1\dots a_{k-1}\) 均置零并删掉 \(a_k\) 最高位并递归实现判定

    递归次数是 \(\Theta(\sum L)\) 的,置零操作并不需要真的置零,只是需要查询的 \(\max\{i+t_i\}\) 的区间发生了变化而已

    使用线段树维护 \(i+t_i\) 的最大值以及最靠左的出现位置,实现上有一个技巧是把 \(i+t_i\) 中的 \(i\) 拆成每个合法叶子上 \(1\) 的累加

    Code Display
    const int N=6e5+10;
    int ones,mxl,n,len[N];
    int head[N],nxt[N],id[N],tmp[N];
    char ans[N];
    string s[N];
    bool vis[N];
    struct node{
        int mx,sum,pos; node(){mx=sum=pos=0;}
        node(int a,int b,int c){mx=a; sum=b; pos=c;}
        node operator +(const node &a)const{
            node t; t.sum=sum+a.sum;
            if(mx>=sum+a.mx) t.mx=mx,t.pos=pos;
            else t.mx=sum+a.mx,t.pos=a.pos;
            return t;
        }
    }t[N<<2];
    #define ls p<<1
    #define rs p<<1|1
    #define lson p<<1,l,mid
    #define rson p<<1|1,mid+1,r
    inline void push_up(int p){ t[p]=t[ls]+t[rs];}
    inline void build(int p,int l,int r){
        if(l==r){
            if(vis[l]) t[p]=node(len[l]+1,1,l);
            return ;
        } int mid=(l+r)>>1;
        build(lson); build(rson);
        return push_up(p);
    }
    inline void flip(int pos,int p=1,int l=1,int r=ones){
        if(l==r){
            if((vis[l]^=1)) t[p]=node(len[l]+1,1,l);
            else t[p]=t[0];
            return ;
        }
        int mid=(l+r)>>1;
        if(pos<=mid) flip(pos,lson); else flip(pos,rson);
        return push_up(p);
    }
    inline node query(int st,int ed,int p=1,int l=1,int r=ones){
        if(st<=l&&r<=ed) return t[p];
        int mid=(l+r)>>1; node ret=t[0];
        if(st<=mid) ret=ret+query(st,ed,lson);
        if(ed>mid) ret=ret+query(st,ed,rson);
        return ret;
    }
    inline bool solve(int l,int h){
        if(l>ones) return 1;
        node cur=query(l,ones);
        if(!cur.sum) return 1;
        // All zero
        if(cur.mx>h) return 0;
        // highest bit illegal
        if(nxt[cur.pos]) flip(nxt[cur.pos]);
        bool legal=solve(cur.pos+1,len[cur.pos]);
        if(nxt[cur.pos]) flip(nxt[cur.pos]);
        if(legal){
            for(int i=len[cur.pos];i<cur.mx;++i) ans[i+1]='1';
            return 1;  
        }
        if(cur.mx>=h) return 0;
        //delete the highest number
        solve(cur.pos+1,len[cur.pos]+1);
        for(int i=len[cur.pos]+1;i<=cur.mx;++i) ans[i+1]='1';
        return 1;
    }
    signed main(){
        freopen("wait.in","r",stdin); freopen("wait.out","w",stdout);
        n=read();
        for(int i=1;i<=n;++i){
            cin>>s[i];
            int slen=s[i].length();
            reverse(s[i].begin(),s[i].end());
            for(int j=0;j<slen;++j) ones+=s[i][j]=='1';
            ckmax(mxl,slen);
        }
        //radix sort
        for(int i=1;i<=n;++i) id[i]=i;
        int rem=n,pter=ones;
        for(int l=0;l<mxl;++l){
            int ord0=0,ord1=0;
            for(int j=1;j<=rem;++j) ord1+=s[id[j]][l]=='0';
            for(int j=1;j<=rem;++j){
                if(s[id[j]][l]=='0') nxt[++ord0]=id[j];
                else nxt[++ord1]=id[j];
            }
            rep(i,1,rem) id[i]=nxt[i];
            for(int i=1;i<=rem;++i){
                if(s[id[i]][l]=='1'){
                    nxt[pter]=head[id[i]];
                    head[id[i]]=pter;
                    len[pter--]=l;
                }
            }
            int cnt=rem; rem=0;
            for(int j=1;j<=cnt;++j){
                if(s[id[j]].length()>l+1) id[++rem]=id[j];
            }
        }
        for(int i=1;i<=n;++i) vis[head[i]]=1;
        build(1,1,ones);
        int Len=n+mxl;
        rep(i,1,Len) ans[i]='0';
        solve(1,Len);
        while(Len>1&&ans[Len]!='1') --Len;
        for(int i=Len;i>=1;--i) putchar(ans[i]); putchar('\n');
        return 0;
    }
    

  • 相关阅读:
    Spring Boot 配置加载顺序详解
    JVM总结篇
    nginx负载均衡的策略
    布隆过滤器的方式解决缓存穿透问题
    Spring Cloud Eureka自我保护机制(服务无法剔除)
    缓存穿透,缓存击穿,缓存雪崩解决方案分析
    高并发秒杀系统总结
    Linux环境进程间通信(一)
    HDU 1695 GCD(容斥定理)
    数据结构精要------冒泡与直接排序算法
  • 原文地址:https://www.cnblogs.com/yspm/p/16073455.html
Copyright © 2020-2023  润新知