【考试总结】2022-03-29

交通

倒序预处理 到达每个路口的时间为绿灯第 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;
}

选拔

诈 骗 大 师

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

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

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

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

同时全局维护一个长度为拼接串串长的 bitset,其对应字符串上字符的位置表示用上述前后缀的 DP 值是否可以 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 是否能作为合法的 b,简记 a1a2an

  • 如果 x 的最高位比 amax 的最高位高那么删掉 amax 变成子问题

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

  • 如果 x 的最高位比 amax 的最高位低那么不合法

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

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

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

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

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

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

现在考虑到 b 的第 p 位,仍然设 a1a2an,同时记 ti 表示 ai 的最高位,记 k=min{argmax{i+ti}}

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

但是仍然有一个 p=k+tkcase 需要处理,那么 ppk, 并将 a1ak1 均置零并删掉 ak 最高位并递归实现判定

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

使用线段树维护 i+ti 的最大值以及最靠左的出现位置,实现上有一个技巧是把 i+ti 中的 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;
}

posted @   没学完四大礼包不改名  阅读(116)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示