[整理]NOIP2021 题解

T1

秒了,直接写一个线性筛一样的东西即可。

const int N=10000010;
int T,x;
bool ok[N];int nxt[N];
il void Init(){
  for(int i=1;i<N;i++){
    if(ok[i])continue;
    int t=i;bool ff=0;
    while(t){
      if(t%10==7){
        ff=1;break;
      }
      t/=10;
    }
    if(ff)for(int j=i;j<N;j+=i)ok[j]=1;
  }
  //lst: 上一个 0
  for(int i=1,lst=0;i<N;i++){
    if(ok[i])continue;
    nxt[lst]=i,lst=i;
  }
}
void Print(int x){
  if(x>9)Print(x/10);
  putchar(x%10+48);
}
int main(){
  Init(),Read(T);
  while(T--){
    Read(x);
    if(!nxt[x])puts("-1");
    else Print(nxt[x]),putchar('\n');
  }
  KafuuChino HotoKokoa
}

T2

题面看起来很麻烦,其实转化成 \(m+1\) 个位置放 \(n\) 个数就很好做了(也就是看从 \(0\)\(m\) 的每个数在 \(a\) 中出现多少次),可以直接设 \(f_{i,j,k,l}\) 表示放到第 \(i\) 位、放了 \(j\) 个、进位 \(k\)、共有 \(l\)\(1\) 时的方案数,转移时乘上系数。

const int N=35,M=110,p=998244353;
int n,m,K,v[M],fac[N],inv[N];
il int Pow(int a,int b=p-2){
  int res=1;
  for(;b;a=(LL)a*a%p,b>>=1)if(b&1)res=(LL)res*a%p;
  return res;
}
int C[N][N];
int f[M][N][N][M];
int main(){
  Read(n),Read(m),Read(K),C[0][0]=1;
  for(int i=0;i<=m;i++)Read(v[i]);
  for(int i=1;i<=n;i++){
    C[i][0]=1;
    for(int j=1;j<=i;j++){
      C[i][j]=(C[i-1][j]+C[i-1][j-1])%p;
    }
  }
  for(int i=0;i<=n;i++){
    f[0][i][i/2][i%2]=(LL)C[n][i]*Pow(v[0],i)%p;
  }
  for(int i=0;i<m;i++){
    for(int j=0;j<=n;j++){
      for(int k=0;k<=j/2;k++){
        for(int l=0;l<=min(i+1,j);l++){
          if(!f[i][j][k][l])continue;
          for(int t=0;j+t<=n;t++){
            //i+1 rT rfT t Vga
            (f[i+1][j+t][k+t>>1][l+(k+t)%2]+=(LL)\
            f[i][j][k][l]*C[n-j][t]%p*Pow(v[i+1],t)%p)%=p;
          }
        }
      }
    }
  }
  int ans=0;
  for(int k=0;k<=n/2;k++){
    for(int l=0;l<=min(m+1,n);l++){
      if(__builtin_popcount(k)+l<=K){
        (ans+=f[m][n][k][l])%=p;
      }
    }
  }
  printf("%d\n",ans);
  KafuuChino HotoKokoa
}

T3

观察题目中的操作,发现其实是交换两个差分值,猜一手结论差分数组应该是单谷。
所以我们可以把差分数组 \(d\) 从小到大往两边加,可以通过 dp 来实现,状态需要记录上当前的 \(a\) 之和。
一眼看上去好像是差分值个数 \(n\) 乘上最大的 \(a\) 之和 \(nV\) 的?不过发现一个细节就是 \(n\) 最大的一档部分分 \(V\) 非常小,也就意味着差分值有贡献(大于零)的个数不会特别多,仍然是 \(O(V)\) 级别的,所以复杂度正确。

const int N=10010,M=610;
int n,a[N],d[N],f[N*M];
il void Mn(int &a,int b){
  a=b<a?b:a;
}
signed main(){
  Read(n);
  for(int i=1;i<=n;i++)Read(a[i]),d[i-1]=a[i]-a[i-1];
  sort(d+1,d+n);
  int na=0,V=0;//dTjuV $\sum d_i$ / zVdaVH6
  memset(f,0x3f,sizeof f),f[0]=0;
  for(int i=1;i<n;i++){
    if(!d[i])continue;
    for(int j=V;j>=0;j--){
      if(f[j]==INF)continue;
      int fj=f[j];f[j]=INF;
      //Eti2ja FLH robie
      Mn(f[j+i*d[i]],fj+2*d[i]*j+i*d[i]*d[i]);
      //Eti2ja FLH 7i6bie
      Mn(f[j+na+d[i]],fj+(na+d[i])*(na+d[i]));
    }
    na+=d[i],V+=i*d[i];
  }
  int ans=INF;
  for(int i=V;i>=0;i--)if(f[i]!=INF)Mn(ans,n*f[i]-i*i);
  printf("%lld\n",ans);
  KafuuChino HotoKokoa
}

T4

快退役了翻草稿箱翻到这玩意,早就忘了这题是啥了,先咕咕着。

posted @ 2023-03-13 20:56  ajthreac  阅读(53)  评论(0编辑  收藏  举报