【考试总结】2022-03-14


神必的集合

题目里面说的 神必的集合 本质上就是线性空间,数线性空间和计算 最简线性基 是等价的

不难发现最后的 \(\langle v_1,\dots v_m\rangle\subseteq S\),所以可以将题目里面给出的 \(v_i\) 建立线性空间

\(f_{i,j}\) 表示考虑了 \(1\sim i\) 这些位置的元的情况,已经放置的主元个数为 \(j\) 的方案数

对于一个新的位置,如果它在原来已经有的线性基里面有主元,那么它必须放置一个主元,主元的值也和已有的线性基中对应主元的值一样,方案数是 \(1\)

否则可以放置自由元,方案数是 \(1\);也可以放置主元,值在这位必须是 \(i\),但是前面的自由元位置上可以任选,且前面的主元必须是 \(0\) 可以得到方案数

注意还要判定这位填主元是不是合法,这个根据线性基求 \(k\) 大的逆过程来考虑,如果限制的排名 \(\rm rank\) 的第 \(j\) 位和 \(v\) 的第 \(i\) 位不一样那么无法满足限制了

需要将 \(rank\leftarrow rank-1\) 来进行后续操作,最后的主元数量是不能少于最大的 \(rank\) 的最高位的,这是因为否则能异或出来的数字的个数根本不够就提不上排名了

时间复杂度 \(\Theta(nm+n^2)\)

Code Display
const int N=200;
int x[N],y[N],b[N],n,m,dp[N][N];
inline void ins(int x){
    for(int i=n;i>=1;--i) if(x>>(i-1)&1){
        if(~b[i]) x^=b[i];
        else return b[i]=x,void();
    }
}
signed main(){
    freopen("set.in","r",stdin); freopen("set.out","w",stdout);
    n=read(); m=read();
    memset(b,-1,sizeof(b));
    int hig=-1;
    rep(i,1,m){
        x[i]=read()-1,y[i]=read();
        if(!x[i]&&y[i]) puts("0"),exit(0);
        if(!y[i]&&x[i]) puts("0"),exit(0);
        if(!x[i]){--i,--m; continue;}
        ins(y[i]);
        hig=max(hig,63ll-__builtin_clzll(x[i]));
    }
    vector pw(100); pw[0]=1;
    for(int i=1;i<100;++i) pw[i]=add(pw[i-1],pw[i-1]);
    dp[0][0]=1;
    for(int i=0;i>i&1)==(x[c]>>j&1);
            if(~b[i+1]){
                if(corr) ckadd(dp[i+1][j+1],dp[i][j]);
            }else{
                if(corr) ckadd(dp[i+1][j+1],mul(dp[i][j],pw[i-j])); //positions : i+1; vectors: j+1; undecided: i-j;
                ckadd(dp[i+1][j],dp[i][j]);
            }
        }
    }
    int ans=0;
    for(int i=hig+1;i<=n;++i) ckadd(ans,dp[n][i]);
    print(ans);
    return 0;
}

法阵

可行的 \((x,y)\) 一定满足 \(a_x,a_y\ge \max\limits_{i=l+1}^{r-1}\{ a_i \}\),否则可以经过固定左端点可以让 \(y-x\) 变小的同时和更大

维护一个单调递减的栈,那么合法的 \((x,y)\) 的对数出现在:将 \(y\) 压入栈的时候,弹出的若干元素;如果 \(y\) 最后出现在单调栈里面,那么它和它前面的一个元素也满足条件

每个元素只会被弹出一次,同时最后单调栈中相邻的元素对数 \(\le n\),所以总的 \((x,y)\)\(\Theta(n)\) 级别的

从右向左扫描线至 \(i\),使用线段树对每个 \(x=i\) 的对找到合法的 \(z\) 即可,使用一个支持查询区间最大值和区间取 \(\max\) 的数据结构即可

Code Display
const int N=5e5+10;
int n,a[N],stk[N],top;
struct Seg{
    #define ls p<<1
    #define rs p<<1|1
    #define lson p<<1,l,mid
    #define rson p<<1|1,mid+1,r
    int self[N<<2],Mx[N<<2],tag[N<<2];
    inline void build(int p,int l,int r){
        if(l==r) return self[p]=a[l],void(); int mid=(l+r)>>1;
        build(lson); build(rson);
        self[p]=max(self[rs],self[ls]);     
    }
    inline void push_up(int p){Mx[p]=max(Mx[ls],Mx[rs]);}
    inline void push_tag(int p,int v){
        ckmax(Mx[p],self[p]+v);
        ckmax(tag[p],v);
        return ;
    }
    inline void push_down(int p){
        if(tag[p]){
            push_tag(ls,tag[p]);
            push_tag(rs,tag[p]);
            tag[p]=0;
        } return ;
    }
    inline void upd(int st,int ed,int v,int p=1,int l=1,int r=n){
        if(st<=l&&r<=ed) return push_tag(p,v);
        int mid=(l+r)>>1; push_down(p);
        if(st<=mid) upd(st,ed,v,lson); if(ed>mid) upd(st,ed,v,rson);
        return push_up(p);
    }
    inline int query(int st,int ed,int p=1,int l=1,int r=n){
        if(st<=l&&r<=ed) return Mx[p]; 
        int mid=(l+r)>>1; push_down(p);
        if(ed<=mid) return query(st,ed,lson); if(st>mid) return query(st,ed,rson);
        return max(query(st,ed,lson),query(st,ed,rson));
    }
    #undef ls
    #undef rs
    #undef lson
    #undef rson
}T;
vector Pair[N];
int ans[N];
vector >qu[N];
signed main(){
    freopen("fz.in","r",stdin); freopen("fz.out","w",stdout);
    n=read();
    rep(i,1,n) a[i]=read(); T.build(1,1,n);
    for(int i=1;i<=n;++i){
        while(top&&a[stk[top]]<=a[i]) Pair[stk[top--]].push_back(i);
        if(top) Pair[stk[top]].emplace_back(i);
        stk[++top]=i;
    }
    int Q=read(); 
    for(int i=1;i<=Q;++i){
        int l=read(),r=read();
        qu[l].emplace_back(r,i);
    }
    for(int i=n;i>=1;--i){
        while(Pair[i].size()){
            int y=Pair[i].back(); Pair[i].pop_back();
            if(2*y-i<=n) T.upd(2*y-i,n,a[i]+a[y]);
        }
        for(auto q:qu[i]) ans[q.sec]=T.query(i,q.fir);
    }
    rep(i,1,Q) print(ans[i]);
    return 0;
}

旅行

按照 传染 一题一样的方法建出点分树,只不过这次在堆里面排序的比较关键字变成了 dis[i]+c[i],松弛没被求出 \(\rm dis\) 的点即可解决 \(m=n-1\) 的部分

对于经过非树边的更新,它一定经过了非树边的某个端点,对每个非树边的某个端点求出来每个点到其的距离,\(x\) 能松弛 \(y\) 的判定方式是 d[x]>=dis[u][x]+dis[u][y]

和点分树一样排序,使用指针维护即可,没有写 \(\Theta(1)\text {LCA}\) 所以复杂度是 \(\Theta(n\log^2n+n(m-n))\)

Code Display
const int N=2e5+10;
vector G[N];
int c[N],d[N],n,m,dis[N];
int f[N],siz[N],rt,nsum,fa[N],pter[N];
bool vis[N];
vector > sub[N];
struct Distance_Query{
    int fa[N],dep[N],top[N],siz[N],son[N],tim,dfn[N];
    inline void dfs1(int x,int fat){
        dep[x]=dep[fa[x]=fat]+(siz[x]=1);
        for(auto t:G[x]) if(t!=fat){
            dfs1(t,x); siz[x]+=siz[t];
            if(siz[t]>siz[son[x]]) son[x]=t;
        } return ;
    }
    inline void dfs2(int x,int topf){
        top[x]=topf; dfn[x]=++tim; if(son[x]) dfs2(son[x],topf);
        for(auto t:G[x]) if(!dfn[t]) dfs2(t,t);
        return ;   
    }
    inline int lca(int x,int y){
        while(top[x]!=top[y]){
            if(dep[top[x]] >q; 
inline void slack(int x){
    int cur=x;
    while(cur){
        int d1=Dis.ask(x,cur);
        if(d1<=d[x]){
            while(pter[cur]d[x]) break;
                int node=sub[cur][pter[cur]].fir;
                if(!vis[node]){
                    vis[node]=1;
                    dis[node]=dis[x]+c[x];
                    q.push(make_pair(-dis[node]-c[node],node));
                }
                ++pter[cur];
            }
        }
        cur=fa[cur];
    } return ;
}
int untu[60],indic[60],untv[60],unt;
signed disu[60][N],ord[60][N],dfn[60][N];
vector g[N];
signed main(){   
    freopen("travel.in","r",stdin); freopen("travel.out","w",stdout);
    n=read(); m=read(); Dsu.init();
    for(int i=1;i<=m;++i){
        int u=read(),v=read();
        g[u].emplace_back(v);
        g[v].emplace_back(u);
        if(Dsu.find(u)!=Dsu.find(v)){
            G[u].push_back(v); G[v].push_back(u);    
            Dsu.merge(u,v);
        }else{
            untu[++unt]=u; untv[unt]=v;
        }
    }
    for(int id=1;id<=unt;++id){
        for(int i=1;i<=n;++i) disu[id][i]=0x3f3f3f3f;
        queue Que; Que.push(untu[id]);
        disu[id][untu[id]]=0;
        while(Que.size()){
            int fr=Que.front(); Que.pop();
            dfn[id][ord[id][++indic[id]]=fr]=indic[id];
            for(auto t:g[fr]) if(disu[id][t]>disu[id][fr]+1){
                disu[id][t]=disu[id][fr]+1;
                Que.push(t);
            }
        }
        indic[id]=1;
    }

    rep(i,1,n) d[i]=read(),c[i]=read();
    Dis.init();
    nsum=n; f[0]=n+1;
    findrt(1,0);
    build(rt);
    memset(vis,0,sizeof(vis));
    memset(dis,0x3f,sizeof(dis)); dis[vis[1]=1]=0;
    q.push(make_pair(dis[1]+c[1],1));
    while(q.size()){
        int fr=q.top().sec; q.pop();
        slack(fr);
        for(int id=1;id<=unt;++id){
            int d1=disu[id][fr];
            if(d[fr]>=d1){
                while(indic[id]<=n){
                    int node=ord[id][indic[id]];
                    if(disu[id][node]+d1>d[fr]) break;
                    if(!vis[node]){
                        vis[node]=1;
                        dis[node]=dis[fr]+c[fr];
                        q.push(make_pair(-dis[node]-c[node],node));
                    }
                    ++indic[id];
                }   
            }
        }
    }
    rep(i,2,n) print(dis[i]);
    return 0;
}