NOIP2021 部分题目整理

(在 NOIP2022 前夕整理 NOIP2021。)

(真的太逊了。)

P7960 报数 Number

解法

由于题目中的因数的限制,考虑埃式筛。在埃式筛中如果遇到某个尚未被标记为非法的数时,如果该数本身包含 \(7\),则需要对其所有倍数标记为非法。由于每个数最多不会被标记超过 \(5\) 次(\(7\times 17\times 27\times 37\times 47\times 57>3\times 10^8\)),故整个过程时间复杂度很小,可以说是小常数的 \(O(x\log x)\)时间复杂度为 \(O(x\log x)\) 的证明)。

代码

点此查看代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=10001000;
inline bool p(int x){
    while(x){
        if(x%10==7) return 1;
        x/=10;
    }
    return 0;
}
int n,i,j,l;
int nxt[maxn];
int main(){
    for(i=1;i<maxn;++i){
        if(nxt[i]) continue;
        if(p(i)) for(j=i;j<maxn;j+=i) nxt[j]=-1;
        if(!nxt[i]) l=nxt[l]=i;
    }
    scanf("%d",&n);
    while(n--){
        scanf("%d",&l);
        printf("%d\n",nxt[l]);
    }
    return 0;
}

P7961 数列 Sequence

解法

直接状压 dp(\(S\le 30\times 2^{12}\)),可得 \(50\) 分。

考虑从 \(v_0\) 考虑到 \(v_m\),将 \(2^0\sim 2^m\) 依次加入 \(S\)。在考虑到 \(v_i\) 时,我们就只需要关心 \(S\bmod 2^i\) 有多少个 \(1\),与 \(\lfloor S/2^i \rfloor\) 的值(涉及到进位等内容)。

注意 \(\{a_i\}\) 不一定是非降的。

推式子可得如下结论:

\(dp_{i,j,k,p}\) 表示考虑到 \(v_i\) (可以把这一维用滚动数组的方式优化成 \(2\)),使用了 \(j\) 个数,\(S\!\! \mod 2^i\)\(k\)\(1\)\(\lfloor S/2^i \rfloor\)\(p\)\(\sum\prod_{q=1}^i v_{a_q}\),则转移有

\[dp_{i,j+c,k+p\bmod 2,\lfloor\frac{p}2\rfloor+c}\ +\!\!=dp_{i-1,j,k,p}\binom c{j+c}v_i^c \]

时间复杂度:\(\mathcal{O}(n^4m)\),可以通过此题。

代码

醒醒大清无了

点此查看代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=40;
const int maxm=100;
const ll md=998244353;
int n,m,k,i,j,p,q,r,s;
bool x,y=1;
ll v,cf,tmp,C[maxn][maxn];
ll dp[2][maxn][maxm][maxn];
inline void Add(ll &_x,const ll _y){
    _x+=_y;
    if(_x>=md) _x%=md;
}
int main(){
    freopen("sequence.in","r",stdin);
    freopen("sequence.out","w",stdout);
    C[0][0]=1;
    for(i=1;i<maxn;++i){
        for(j=0;j<=i;++j){
            Add(C[j][i],C[j][i-1]+C[j-1][i-1]);
        }
    }
    scanf("%d%d%d",&n,&m,&k);
    dp[0][0][0][0]=1;
    for(i=0;i<=m;++i){
        scanf("%lld",&v);
        for(r=0;r<=n;++r){
            cf=1;
            for(p=0;p<=r;++p){
                for(j=0;j<maxm;++j){
                    s=j&1;
                    for(q=0;q<=k;++q){
                        tmp=dp[x][r-p][j][q]*cf;
                        if(tmp>=md) tmp%=md;
                        Add(dp[y][r][(j>>1)+p][s],tmp*C[p][r]);
                        if((++s)>k) break;
                    }
                }
                cf*=v;
                if(cf>=md) cf%=md;
            }
        }
        memset(dp[x],0,sizeof(dp[x]));
        swap(x,y);
    }
    tmp=0;
    for(i=0;i<maxm;++i){
        if(!i) s=0;
        else s=__builtin_popcount(i);
        for(j=0;j<=k;++j){
            if(s>k) break;
            Add(tmp,dp[x][n][i][j]);
            ++s;
        }
    }
    printf("%lld",tmp);
    return 0;
}

P7962 方差 Variance

解法

考虑在将 \(a_i\) 变为 \(a_{i-1}+a_{i+1}-a_i\) 之后的影响:\((a_{i-1}+a_{i+1}-a_i)-a_{i-1}=a_{i+1}-a_i,a_{i+1}-(a_{i-1}+a_{i+1}-a_i)=a_i-a_{i-1}\),相当于交换了 \(i+1\)\(i\) 处的差分。记 \(d_i=a_i-a_{i-1}\),则 \(d_2\sim d_n\) 在这些操作后可以任意排列。

此时对于某个平均数 \(S\),如果某两个数 \(a_i,a_{i+1}\) 均小于 \(S\)\(d_{i+1}>d_i\),则将 \(d_i,d_{i+1}\) 交换后 \(a_i\) 值会变大但不超过 \(a_{i+1}\)\(a_i\) 将会更接近 \(S\),方差会更小。故最后的 \(d\) 一定会呈现单谷。

定义 \(S=\sum_{j=1}^n a_j\),则原式可以简化如下:

\[\begin{aligned}n^2D&=n\sum_{i=1}^n(a_i-\frac Sn)^2\\&=n\sum_{i=1}^n(a_i^2-\frac {2a_iS}n+\frac {S^2}{n^2})\\&=n\sum_{i=1}^n a_i^2-2S(\sum_{i=1}^n a_i)+S^2\\&=n\sum_{i=1}^n a_i^2-S^2\end{aligned} \]

考虑从单谷部分开始向两边 dp,设 \(dp_{i,j}\) 为考虑了前 \(i\) 个差分,且当前 \(a\) 值之和为 \(j\) 时所有 \(a\) 的平方和最小值。此时对每个 \(a\) 值均减去 \(a_1\) 不会对答案造成影响,可以设 \(a_1=0\),也就是初值为 \(dp_{1,0}=0,\forall i\ne 0,dp_{1,i}=+\infty\)。转移时,如果新的 \(d_i\) 放在最左端,则其将其他每个 \(a\) 值均增加了 \(d_i\),新的 \(a\) 值也是 \(d_i\);如果新的 \(d_i\) 放在最右端,则这个新增的 \(a\) 值为 \(\sum_{j=2}^i d_j\),而其他 \(a\) 值不会被影响。设 \(s_i=\sum_{j=2}^i d_j\),则转移有:

\[dp_{i,j+d_ii}\leftarrow\min(dp_{i,j+d_ii},dp_{i-1,j}+2d_ij+d_i^2(i-1))\\dp_{i,j+s_i}\leftarrow\min(dp_{i,j+s_i},dp_{i-1,j}+s_i^2) \]

第一维可以使用背包的类似优化方式优化掉。考虑 \(d_i=0\) 时一定有 \(s_i=0\),转移只会有 \(dp_{i,j}=\min(dp_{i,j},dp_{i-1,j})\),则可以把 \(d_i=0\) 的部分跳过,则最后有用的 \(d\) 值只会有 \(\min(n,a)\) 个,故优化后的时间复杂度为 \(O(\min(n,a)na)\)

代码

正解甚至比 8 pts 暴力短()

点此查看代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=10010;
const int maxv=500010;
int n,i,j,s,v,d[maxn];
ll a=1e14,dp[maxv];
inline void cmin(ll &x,ll y){if(y<x) x=y;}
int main(){
    freopen("variance.in","r",stdin);
    freopen("variance.out","w",stdout);
    scanf("%d",&n);
    for(i=1;i<=n;++i) scanf("%d",d+i);
    for(i=n;i;--i) d[i]-=d[i-1];
    for(j=0;j<maxv;++j) dp[j]=1e14;
    sort(d+2,d+n+1); dp[0]=0;
    if(!d[n]){printf("0\n");return 0;}
    for(i=2;i<=n;++i){
        if(!(s+=d[i])) continue; v=(i-1)*d[i];
        for(j=maxv-1;~j;--j){
            if(j<maxv-v) cmin(dp[j+v],dp[j]+1LL*d[i]*v+2*d[i]*j);
            if(j<maxv-s) cmin(dp[j+s],dp[j]+s*s); dp[j]=1e14;
        } 
    }
    for(j=1;j<maxv;++j) a=min(a,dp[j]*n-1LL*j*j);
    printf("%lld",a);
    return 0;
}

P7963 棋局 Chess

解法(暂缺)

代码(暂缺)

点此查看代码

posted @ 2022-11-21 15:55  Fran-Cen  阅读(85)  评论(0编辑  收藏  举报