首页 > 代码库 > CodeForces 632E Thief in a Shop

CodeForces 632E Thief in a Shop

题意:给你n种物品,每种无限个,问恰好取k个物品能组成哪些重量.n<=1000,k<=1000,每种物品的重量<=1000.

我们搞出选取一种物品时的生成函数,那么只要对这个生成函数求k次幂就可以了.结果会很大所以我们可以在模意义下NTT来搞.然而会有一个问题,就是算出来的系数可能恰好是模数的倍数,比如说我们只用998244353就会WrongAnswer on test 20.然后我试了和异化多肽的模数1005060097一块来,然后,WrongAnswer on test 5…Excuse me? 然后看数据,k=1000,物品重量也比较大,分解一下1005060096,发现是2^19*27*71,然后我意识到了:这道题最大需要做最高次数2^20的NTT…然后枚举出了几个2^22*k+1的质数做模数…过了…

以下是枚举出来的一些模数和原根,均为2^22*k+1的形式(不保证k为奇数)

104857601 3

113246209 7

138412033 5

155189249 6

163577857 23

167772161 3

230686721 6

377487361 7

415236097 5

469762049 3

595591169 3

645922817 3

666894337 5

683671553 3

754974721 11

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int mod,rt;
int qpow(int a,int x){int ans=1;for(;x;x>>=1,a=a*1ll*a%mod)if(x&1)ans=ans*1ll*a%mod;return ans;}
void ntt(int *a,int n,int sign){
  for(int i=1,j=0,k;i<n;++i){
    k=n;do j^=(k>>=1);while(j<k);if(i<j)swap(a[i],a[j]);
  }
  for(int j=2;j<=n;j<<=1){
    int m=j>>1;long long wn=qpow(rt,(mod-1+sign*(mod-1)/j));;
    for(int *p=a;p!=a+n;p+=j){
      long long w=1;
      for(int k=0;k<m;++k,w=w*wn%mod){
    long long t=w*p[m+k]%mod;p[m+k]=(p[k]-t+mod)%mod;p[k]=(p[k]+t)%mod;
      }
    }
  }
  if(sign==-1){
    long long inv=qpow(n,mod-2);for(int i=0;i<n;++i)a[i]=a[i]*inv%mod;
  }
}
const int maxn=1048576*4;
int a[maxn],w[maxn];
int ans[maxn];
int N; int n,k;
void NTT(int MOD,int RT){
  mod=MOD;rt=RT;memset(a,0,sizeof(a));
  for(int i=1;i<=n;++i)a[w[i]]=1;
  ntt(a,N,1);
  for(int i=0;i<N;++i)a[i]=qpow(a[i],k);
  ntt(a,N,-1);//printf("%d\n",ans[1]);
  for(int i=0;i<N;++i)if(a[i])ans[i]=1;
}
int main(){
  scanf("%d%d",&n,&k);
  int Max=0;
  for(int i=1;i<=n;++i){scanf("%d",&w[i]);if(w[i]>Max)Max=w[i];}
  N=1;
  while(N<=Max*k)N<<=1;
  //printf("%d\n",ans[0]);
  NTT(998244353,3);NTT(163577857,23);NTT(415236097,5);
  for(int i=0;i<N;++i)if(ans[i])printf("%d ",i);
  return 0;
}

 

CodeForces 632E Thief in a Shop