1. 程式人生 > 其它 >【考試總結】2022-04-12

【考試總結】2022-04-12

史萊姆A

\(f_i\) 表示前 \(i\) 個元素進行劃分得到的 \(f\) 函式之和,轉移列舉最後一段劃分在哪裡

根據 \(a_i\le 10\) 覺得部分可以發現每次轉移將 \(\rm mex\) 相同的一起做是減少冗餘的一個方式

但是顯然可以再給力一些,使用線段樹容易在挪動右端點時維護每個左端點為起點時這段的 \(\rm mex\) ,把 \(f\) 值掛到葉子上那麼就是區間求和,也能應付 \(\rm mex\) 的修改

被卡常可以將單點修改寫成 \(\rm zkw\) 的形式

Code Display
const int mod=998244353;
inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline int mul(int x,int y){return x*y-x*y/mod*mod;}
const int N=1e6+10;
int a[N],n,Q,app[N];
struct node{
    int mex,l,r;
    bool operator <(const node &a)const{return mex<a.mex;}
};
set<node>now;
#define ls p<<1
#define rs p<<1|1
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
struct segment_tree{
    int Mn[N<<2];
    inline void push_up(int p){Mn[p]=min(Mn[ls],Mn[rs]);}
    inline void modify(int pos,int v,int p=1,int l=0,int r=n){
        if(l==r) return Mn[p]=v,void(); 
        int mid=(l+r)>>1;
        if(pos<=mid) modify(pos,v,lson); 
        else modify(pos,v,rson);
        return push_up(p);
    }
    inline int erf(int tar,int v,int p=1,int l=0,int r=n){
        if(Mn[p]>tar) return -1;
        if(l==r) return l; int mid=(l+r)>>1;
        if(v<=l){
            if(Mn[ls]<=tar) return erf(tar,v,lson);
            return erf(tar,v,rson);
        }
        if(v>mid) return erf(tar,v,rson);
        int res=erf(tar,v,lson);
        if(~res) return res; return erf(tar,v,rson);
    } //larger than v,backer than tar
}lst;
struct Segment_Tree{
    int sum[N<<2],val[N<<2],id[N],cov[N<<2];
    inline void build(int p,int l,int r){
        cov[p]=-1;
        if(l==r) return id[l]=p,void(); int mid=(l+r)>>1;
        build(lson); build(rson);
        return ;
    }
    inline void push_cov(int p,int v){
        sum[p]=mul(val[p],cov[p]=v);
        return ;
    }
    inline void push_down(int p){
        if(~cov[p]){
            push_cov(ls,cov[p]);
            push_cov(rs,cov[p]);
            cov[p]=-1;
        } return ;
    }
    inline void push_up(int p){
        val[p]=val[ls]+val[rs];
        sum[p]=sum[ls]+sum[rs];
        return ;   
    }
    inline int query(int ed,int p=1,int l=1,int r=n){
        if(r<=ed) return sum[p]; 
        int mid=(l+r)>>1; push_down(p);
        if(ed<=mid) return query(ed,lson); 
        return query(ed,lson)+query(ed,rson);
    }
    int st,ed,v;
    inline void give_cov(int p=1,int l=1,int r=n){
        if(st<=l&&r<=ed) return push_cov(p,v);
        int mid=(l+r)>>1; push_down(p); 
        if(st<=mid) give_cov(lson);
        if(ed>mid) give_cov(rson);
        return push_up(p);
    }
    inline void g_cov(int l,int r,int V){
        st=l; ed=r; v=V;
        give_cov();
    }
    inline void modify(int pos,int v){
        int p=id[pos];
        val[id[pos]]=v;
        while(p>>=1) push_up(p);
    }
}seg;
#undef ls 
#undef rs
#undef lson
#undef rson
int dp[N];
signed main(){
    freopen("a.in","r",stdin); freopen("a.out","w",stdout);
    n=read(); rep(i,1,n) a[i]=read();
    seg.build(1,1,n);
    seg.modify(1,1);
    auto ins=[&](int mex,int l,int r){
        auto iter=now.lower_bound({mex,0,0});
        if(iter==now.end()){
            now.insert({mex,l,r});
            return ;
        }
        int L=iter->l,R=iter->r;
        if(iter->mex==mex) now.erase(iter),now.insert({mex,L,r});
        else now.insert({mex,l,r});
    };
    int qcnt=0;
    for(int i=1;i<=n;++i){
        if(a[i]<=n) app[a[i]]=i,lst.modify(a[i],i);
        auto iter=now.lower_bound({a[i],0,0});
        if(iter!=now.end()){
            if(iter->mex==a[i]){
                int L=iter->l,R=iter->r,lastv=iter->mex;
                now.erase(iter);
                while(L<=R){
                    ++qcnt;
                    int mex=lst.erf(R,lastv+1),pos=max(app[mex]+1,L);
                    if(pos<=R){
                        ins(mex,pos,R);
                        seg.g_cov(pos,R,mex);
                    }
                    lastv=mex;
                    R=app[mex];
                }
            }
        }
        seg.g_cov(i,i,!a[i]);
        ins(!a[i],i,i);
        dp[i]=seg.query(i)%mod;
        if(i<n) seg.modify(i+1,dp[i]);
        else print(dp[i]);
    }
    return 0;
}

史萊姆B

有一些性質可以加以挖掘:

  • \(a\le b\le c,a\oplus c\ge \min(a\oplus b,b\oplus c)\)

  • \(a\le b,b-a\le a\oplus b\)

此時我們發現 \(S\) 中元素從小到達排序之後只有相鄰的元素作為 \((i,j)\) 時才會對答案產生貢獻

\([0,2^V)\) 列舉 \(x\),稱一個 \(x\) 是“對答案有貢獻的” 當且僅當所有 \(y<x\) 都滿足 \((i+y)\oplus (j+y)>(x+i)\oplus (j+x)\) ,那麼對於一對 \((i,j)\) ,對答案有貢獻的 \(x\)

只有 \(\Theta(V)\)

同時這些 \(x\) 屬於 滿足讓 \(i+x\) 或者 \(j+x\) 的後 \(t\le [1,V]\) 位為 \(0\) 的最小可行數 構成的集合中

其實簡單微調 \(x\leftarrow x+k\) ,那麼如果另一個數字不產生更高的進位那麼一定會更大

所以可以列舉所有 \(t\) 得到可行 \(x\),由於只有加入數字的操作,那麼可以使用 std::set 來維護所有的三元組 \((i,j,x)\) 使得權值在 \(x\) 增加時減小

Code Display
set<int> vals;
map<int,int> ans;
int V,n;
inline void insert(int x,int y){
    auto add=[&](const int x,const int y){
        auto iter=ans.upper_bound(x);
        if(iter==ans.begin()) ans[x]=y;
        else{
            --iter;
            if(iter->sec<=y) return ;
            if(iter->fir==x) iter->sec=y,++iter;
            else ++iter,ans[x]=y;
            while(iter!=ans.end()){
                if(y>iter->sec) break;
                ++iter;
                ans.erase(prev(iter));
            }
        }
        return ;
    };
    add(0,x^y);
    for(int i=1;i<=V;++i){
        int z=(1ll<<i)-(x&((1ll<<i)-1));
        add(z,(x+z)^(y+z));
        z=(1ll<<i)-(y&((1ll<<i)-1));
        add(z,(x+z)^(y+z));
    }
    return ;
}
signed main(){
    freopen("b.in","r",stdin); freopen("b.out","w",stdout);
    V=read(); n=read();
    while(n--){
        if(read()-1){
            auto iter=ans.upper_bound(read());
            print(prev(iter)->sec);            
        }else{
            int x=read();
            vals.insert(x);
            auto iter=vals.find(x);
            if(iter!=vals.begin()){
                insert(*iter,*prev(iter));
            }
            if(iter!=prev(vals.end())){
                insert(*iter,*next(iter));
            }
        }
    }
    return 0;
}

史萊姆C

考慮類似 \(\rm LOJ\) 貪玩藍月 一題的重構思想來維護整個過程,那麼直接用 \([0,K-2]\) 來表示當前棧中元素,棧大小不夠的時候直接暴力 \(\rm Prime/Kruskal\)

根據 \(\rm Subtask \ 4\) 可以發現需要維護 \([L,0),[0,K-2],[K-1,R]\) 三部分的最小生成樹,每次查詢進行合併

注意這時候不能使用 \(\rm LCT\) 來維護整個最小生成樹,必須分開維護,最後合併。因為這裡和線段樹分治不同,刪邊的時候會到達之前沒有到達過的狀態

本題做法是直接在左右兩個部分生成樹上面維護 \([0,K-2]\) 元素的虛樹,虛樹上邊權表示真正生成樹上路徑上的權值最大值

此時並不容易進行加刪點,所以對於某個 \(L\) 維護邊集為 \([L,0)\) 的出邊且關鍵點為 \([L,L+K-1]\cup [0,K-2]\) 的虛樹即可

新加入節點時將 新點連出去的邊和上次邊界上的虛樹邊集 使用求出來新的最小生成樹,然後再在當前得到的樹上 \(\rm DFS\) 來刪掉不是關鍵的點

具體而言,每個點維護是否已經在虛樹上/不在的話所在存在關鍵點的鏈的鏈底和鏈上邊權最大值,轉移時如果出現鏈那麼讓 邊集(最小生成樹除去虛樹)的總權值加上較小者並保留較大者

如果存在分叉則這段鏈一定在虛樹裡面,保留較大者並讓權值加上較小者

最後計算答案時也是三部分歸併

Code Display
inline bool in(int x,int L,int R){return x>=L&&x<=R;}
namespace qwq{
    std::mt19937 eng;
    void init(int Seed){eng.seed(Seed);}
    int readW(){return uniform_int_distribution<int>(0,1000000000)(eng);}
}
const int N=1e6+10;
int anc[N],n,Q,K;
inline int find(int x){return anc[x]==x?x:anc[x]=find(anc[x]);}
vector<pair<int,int> >G[N];
int down[N],val[N];
int wei[N][23];
int indl,indr,curl,curr,L,R;
struct edge{
    int u,v,w; edge(){u=v=w=0;}
    edge(int U,int V,int W){u=U; v=V; w=W;}
    bool operator <(const edge &E)const{return w<E.w;}
}vt[N][40],curE[300],vte[300];
int tcnt[N],sum[N];
inline void dfs(int x,int fat,int id){
    down[x]=val[x]=0;
    if(in(x,indl,indr)||in(x,curl,curr)) down[x]=x;
    for(auto e:G[x]) if(e.fir!=fat){
        int t=e.fir;
        dfs(t,x,id);
        if(!down[t]){
            sum[id]+=e.sec;
            continue;
        } // unimportant leaf
        if(!down[x]){
            down[x]=down[t];
            val[x]=max(val[t],e.sec);
            sum[id]+=min(val[t],e.sec);  
            continue;
        } //current node is not important
        if(down[x]^x){
            vt[id][++tcnt[id]]={down[x],x,val[x]};
            down[x]=x;
        }
        vt[id][++tcnt[id]]={x,down[t],max(val[t],e.sec)};
        sum[id]+=min(val[t],e.sec);
    }
    if(down[x]==x) val[x]=0;
    G[x].clear();
    return ;
}
inline void insert(int x,int coef){
    tcnt[x]=sum[x]=0;
    auto set=[&](const int x){anc[x]=x; G[x].clear();};
    int lst=x+coef;
    sum[x]=sum[lst];
    set(x);
    for(int i=1;i<=tcnt[lst];++i){
        set(vt[lst][i].u);
        set(vt[lst][i].v);
    }
    int curcnt=0;
    for(int i=1;i<K;++i){
        set(x+coef*i);
        curE[++curcnt]={x+coef*i,x,wei[x][K+coef*i]};
    }
    sort(curE+1,curE+curcnt+1);
    int indic1=1,indic2=1;
    // one for current linked edges
    // the second for virtual tree x+coef
    while(indic1<=curcnt||indic2<=tcnt[lst]){
        edge tmp;
        if(indic2>tcnt[lst]||(indic1<=curcnt&&curE[indic1]<vt[lst][indic2])) tmp=curE[indic1++];
        else tmp=vt[lst][indic2++];
        if(find(tmp.u)!=find(tmp.v)){
            anc[find(tmp.u)]=find(tmp.v);
            G[tmp.u].emplace_back(tmp.v,tmp.w);
            G[tmp.v].emplace_back(tmp.u,tmp.w);
        }
    }
    curl=x; curr=x+coef*(K-2);
    if(curl>curr) swap(curl,curr);
    dfs(x,0,x);
    sort(vt[x]+1,vt[x]+tcnt[x]+1);
    return ;
}
bool Reb=1;
inline void rebuild(){
    Reb=1;
    if(R-L<K-1) indl=L,indr=R;
    else indl=L+(R-L-K)/2+1,indr=indl+K-2;
    tcnt[indl]=tcnt[indr]=sum[indl]=sum[indr]=0;
    for(int i=indl-1;i>=L;--i) insert(i,1);
    for(int i=indr+1;i<=R;++i) insert(i,-1);
    return ;
}
int vtecnt;
signed main(){
    freopen("c.in","r",stdin); freopen("c.out","w",stdout);
    qwq::init(read()); K=read(); Q=read();
    L=R=indl=indr=Q+1;
    int lst=-1,lans=-1;
    while(Q--){
        int opt=read();
        if(opt==1){
            ++L;
            if(L>indl) rebuild();
        }
        if(opt==2){
            --R;
            if(R<indr) rebuild();
        }
        if(opt==3){
            int num=min(R-L+1,K-1);
            for(int i=1;i<=num;++i) wei[L-1][K+i]=wei[L-1+i][K-i]=qwq::readW();
            --L;
            if(R-L+1<K) rebuild();
            else insert(L,1);
        }
        if(opt==4){
            int num=min(R-L+1,K-1);
            for(int i=1;i<=num;++i) wei[R+1][K-i]=wei[R+1-i][K+i]=qwq::readW();
            ++R;
            if(R-L+1<K) rebuild();
            else insert(R,-1);
        }
        if(opt==5){
            if(lst==5){print(lans); continue;}
            auto set=[&](const int x){anc[x]=x;};
            if(Reb){
                vtecnt=0;
                int curcnt=0;
                for(int i=indl;i<=indr;++i){
                    set(i);
                    for(int j=1;i+j<=indr;++j) vte[++curcnt]={i,i+j,wei[i][K+j]};
                }
                sort(vte+1,vte+curcnt+1);
                for(int i=1;i<=curcnt;++i){
                    int u=vte[i].u,v=vte[i].v,w=vte[i].w;
                    if(find(u)==find(v)) continue;
                    vte[++vtecnt]={u,v,w};
                    anc[find(u)]=anc[v];
                }
            }
            if(indl==L&&indr==R){
                int sum=0;
                for(int i=1;i<=vtecnt;++i) sum+=vte[i].w;
                print(sum);
                continue;
            }
            int curcnt=0,ans=sum[L]+sum[R];
            for(int i=1;i<=tcnt[L];++i) curE[++curcnt]=vt[L][i];
            for(int i=1;i<=tcnt[R];++i) curE[++curcnt]=vt[R][i];
            for(int i=1;i<=vtecnt;++i) curE[++curcnt]=vte[i];
            for(int i=1;i<=curcnt;++i) set(curE[i].u),set(curE[i].v);
            sort(curE+1,curE+curcnt+1);
            for(int i=1;i<=curcnt;++i){
                int u=curE[i].u,v=curE[i].v;
                if(find(u)==find(v)) continue;
                ans+=curE[i].w;
                anc[find(u)]=find(v);
            }
            print(lans=ans);
        }
        lst=opt;
    }
    return 0;
}