【考试总结】2022-03-03


排队

\(nm\)\(\rm DP\) 是显然的,发现每 \(\bmod\) 次转移系数是一致的,那么可以合并计算

有了矩阵快速幂的雏形关键在于计算矩阵中每个位置的系数,要计算 \(x\) 行的系数可以把数组里面序号为 \(x\) 的位置置 \(1\) 做一次 \(\rm Dp\) 来实现

复杂度 \(\Theta(m^3\bmod)\)

Code Display
int n,m,tot,dp[2010][20][2][2],id[11][2][2],vec[50],f[2][20][2][2];
struct Mat{
    int a[50][50]; Mat(){}
}mat;
int a[50][50],tmp[50];
signed main(){
    freopen("queue.in","r",stdin); freopen("queue.out","w",stdout);
    n=read(); m=read(); mod=read();
    dp[2][1][1][0]=dp[2][1][0][1]=1;
    for(int i=1;i<=m;++i){
        rep(j,0,1) rep(k,0,1) id[i][j][k]=++tot;
    }
    int now=2;
    while((n-now)%mod!=0){
        for(int j=1;j<=m;++j){
            rep(k,0,1) rep(l,0,1) if(dp[now][j][k][l]){
                ckadd(dp[now+1][j+(!k)][1][l],dp[now][j][k][l]);
                ckadd(dp[now+1][j+(!l)][k][1],dp[now][j][k][l]);
                int gap=j*2-k-l,rem=now-1-gap; gap%=mod;
                if(k) gap--,ckadd(dp[now+1][j][0][l],dp[now][j][k][l]);
                if(l) gap--,ckadd(dp[now+1][j][k][0],dp[now][j][k][l]);
                ckadd(dp[now+1][j][k][l],mul(dp[now][j][k][l],gap));
                ckadd(dp[now+1][j+1][k][l],mul(dp[now][j][k][l],rem%mod));
            }
        }
        ++now;
    }
    if(now==n){
        int ans=add(add(dp[n][m][0][0],dp[n][m][0][1]),add(dp[n][m][1][0],dp[n][m][1][1]));
        print(ans);        
        exit(0);
    }
    rep(i,1,m) rep(j,0,1) rep(k,0,1) vec[id[i][j][k]]=dp[now][i][j][k];
    int tim=(n-now)/mod;
    rep(x,1,m) rep(y,0,1) rep(z,0,1){
        int cur=0;
        f[cur][x][y][z]=1;
        rep(i,now,now+mod-1){
            for(int j=1;j<=m;++j){
                rep(k,0,1) rep(l,0,1) if(f[cur][j][k][l]){
                    ckadd(f[cur^1][j+(!k)][1][l],f[cur][j][k][l]);
                    ckadd(f[cur^1][j+(!l)][k][1],f[cur][j][k][l]);
                    int gap=j*2-k-l,rem=i-1-gap; gap%=mod;
                    if(k) gap--,ckadd(f[cur^1][j][0][l],f[cur][j][k][l]);
                    if(l) gap--,ckadd(f[cur^1][j][k][0],f[cur][j][k][l]);
                    ckadd(f[cur^1][j][k][l],mul(f[cur][j][k][l],gap));
                    ckadd(f[cur^1][j+1][k][l],mul(f[cur][j][k][l],rem%mod));
                    f[cur][j][k][l]=0;
                }
            } cur^=1;
        }
        rep(i,1,m) rep(j,0,1) rep(k,0,1) mat.a[id[x][y][z]][id[i][j][k]]=f[cur][i][j][k],f[cur][i][j][k]=0;
    }
    while(tim){
        if(tim&1){
            rep(i,1,tot) rep(j,1,tot) ckadd(tmp[j],mul(vec[i],mat.a[i][j]));
            rep(i,1,tot) vec[i]=tmp[i],tmp[i]=0;
        }
        rep(i,1,tot) rep(k,1,tot) rep(j,1,tot) ckadd(a[i][j],mul(mat.a[i][k],mat.a[k][j]));
        rep(i,1,tot) rep(j,1,tot) mat.a[i][j]=a[i][j],a[i][j]=0;
        tim>>=1;
    }
    int ans=add(vec[id[m][0][0]],add(vec[id[m][1][1]],add(vec[id[m][1][0]],vec[id[m][0][1]])));
    print(ans);
    return 0;
}

昵称

使用 \(\rm AC\) 自动机实现一个计算不超过 \(\overline{a_1\dots a_n}\) 的数字中有多少个满足包含给定的 \(S\)

所以我们先二分最后字符串的长度,之后按位确定每个数位是几

注意到一定有连续的一段是 \(S\),那么对于每个自由位置先判定是不是必须要填 \(S\) 即可,判定次数是自由元个数的级别,也就是 \(\log 10^18\)

那么复杂度也就降到了 \(\Theta(n^2\log n)\),常数还是蛮大的

Code Display
const int N=2010;
int n,fail[N],son[N][10],m;
char s[N];
int dp[2][N][2][2],up[N];
//dp[cur][match][bound][pas]
inline bool check(int len){
    memset(dp,0,sizeof(dp));
    int cur=0; dp[cur][0][1][0]=1;
    for(int i=0;i=m&&l) return 1;
                if(!l&&len-i=m) return 1;
        ans+=dp[cur][i][1][1];
        if(ans+dp[cur][i][0][1]>=m) return 1;
        ans+=dp[cur][i][0][1];  
    } return 0;
}
int ans[N];
signed main(){
    freopen("nickname.in","r",stdin); freopen("nickname.out","w",stdout);
    scanf("%s",s+1); n=strlen(s+1); m=read();
    son[0][s[1]-'0']=1;
    for(int i=0;i>1;
        rep(i,1,n+50) up[i]=0;
        rep(i,1,mid) up[i]=9;
        if(check(mid)) r=mid-1,tar=mid;
        else l=mid+1;
    }
    bool Push=0;
    memset(up,0,sizeof(up));
    rep(i,1,tar) ans[i]=9,up[i]=9;
    for(int i=1;i<=tar;++i){
        rep(j,1,tar) up[j]=ans[j];
        if(Push==0&&i+n-1<=tar){
            for(int j=0;j>1; up[i]=mid;
            if(check(tar)) ans[i]=mid,r=mid-1;
            else l=mid+1;
        }
    }
    for(int i=1;i<=tar;++i) putchar(ans[i]+'0'); putchar('\n');
    return 0;
}


帝国防卫

使用整体二分来计算每个点骑士数量不小于 \(c_i\) 的时间,考虑对于每个位置维护 \(Add_i\) 表示距离为 \(i\) 的点在这个点能得到的贡献,同时维护 \(Del_i\) 表示从哪里跳上来的要减掉

注意这里贡献不能写作加和右移,因为这和右移之后再加和是完全不一样的,这也就是为什么每个深度要单独维护数组的原因了

每次修改跳 \(\log\) 个父亲,每个父亲对应修改其深度即可

剩下的都是整体二分和树状数组 \(\rm dfs\) 序的基础操作了,不再赘述

时间复杂度是 \(\Theta(n\log^3n)\)

一个可行的优化方式就是将所有查询和修改的点拉出来建立虚树,虚树上边权就是两个点之间的距离

还是执行上述操作,不过每个虚树上节点维护权值 \(Add_i,Del_i\) 表示它和虚树上父亲这一段里面的点的“三角形”数组里面的权值的和

这个时候增量和原来的一个数字不同,是一个 \(\Sigma \frac{x}{2^i}\) 关于 \(i\) 的区间和,但是仍然可以 \(\Theta(1)\) 求出来

那么使用一个类似换根 \(\rm DP\) 的东西就能求出来正确的权值了,一种可行的实现是不存真值,而是每个数位 \(1\) 的个数

代码实现的 \(3\)\(\log\) 的,所以上面做法是在口嗨

Code Display
const int N=1e5+10;
int n;
struct BIT{
    int c[N];
    inline void insert(int x){for(;x<=n;x+=x&(-x)) c[x]++; return ;}
    inline int query(int x){int res=0; for(;x;x-=x&(-x)) res+=c[x]; return res;}
}T;
int c[N],fa[N],val[N],in[N],out[N],dfn;
vector G[N],node[N];

inline void get_dfn(int x,int fat){
    in[x]=++dfn; fa[x]=fat;
    for(auto t:G[x]) if(t!=fat) get_dfn(t,x);
    out[x]=dfn;
}
int sum[N],Add[N][19],Del[N][19];
int x[N],delt[N],typ[N],tim[N];
inline int Query(int x){
    int res=sum[x],lst=x; x=fa[x];
    int up=1;
    while(up<=18&&x){
        res+=Add[x][up]-Del[lst][up];
        ++up; x=fa[lst=x];
    } return res;
}
inline void insert(int x,int val,int fl){
    int now=x,tmp=val;
    while(now&&tmp) sum[now]+=tmp*fl,tmp>>=1,now=fa[now];
    int lst=0;
    while(val&&x){
        for(int j=1;val>>j;++j) Add[x][j]+=fl*(val>>j),Del[lst][j]+=fl*(val>>j);
        x=fa[lst=x]; val>>=1;
    }
    return ;
}
inline void solve(int ql,int qr,vector now){
    if(ql==qr){
        for(auto t:now) tim[t]=ql;
        return ;
    }
    int mid=(ql+qr)>>1;
    for(int i=ql;i<=mid;++i) if(typ[i]==1) insert(x[i],delt[i],1);
    vector lef,rig;
    for(auto e:now) if(Query(e)>=c[e]) lef.push_back(e); else rig.push_back(e);
    solve(mid+1,qr,rig); 
    for(int i=ql;i<=mid;++i) if(typ[i]==1) insert(x[i],delt[i],-1);
    solve(ql,mid,lef);
    return ;
}
signed main(){
    freopen("empire.in","r",stdin); freopen("empire.out","w",stdout);
    n=read(); rep(i,1,n) c[i]=read();
    for(int i=1;i now;
    for(int i=1;i<=n;++i) if(Query(i)>=c[i]) now.push_back(i); 
    for(int i=1;i<=Q;++i) if(typ[i]==1) insert(x[i],delt[i],-1);
    solve(1,Q,now);
    for(int i=1;i<=n;++i) if(tim[i]) node[tim[i]].push_back(i);
    for(int i=1;i<=Q;++i){
        if(typ[i]==2){
            print(T.query(out[x[i]])-T.query(in[x[i]]-1));
        }else{
            for(auto t:node[i]) T.insert(in[t]);
        }
    }
    return 0;
}

感觉这套题目许多同学现在考和联赛前考得分并不会有什么不一样

相关