惑疑的凯小

https://zybuluo.com/ysner/note/1177710

题面

现有\(n\)个砝码,重量分别为\(a_1,a_2,a_3,...,a_n\)\(xzy\)太好吃了,于是吃掉了其中\(m\)个砝码。
现在\(xzy\)想知道,他在剩余的砝码中随机选取一些砝码(不能不选),正好称出\(k\)的重量的方案数?
但他要我们输出的是答案 除以 \(xzy\)吃掉砝码的方案数,并对\(6249433\)取模。

  • \(30pts\) \(n\leq233,m=0\)
  • \(100pts\) \(n\leq20,m<n,a_i\leq100\)

    解析

    \(30pts\)算法

    显然可以设\(dp[i][sum]\)表示前\(i\)个数中随便取数后和为\(sum\)的方案数。
    则枚举\(i\)\(sum(sum\in[1,k])\),可得转移方程
    \(dp[i][sum+a[i]]+=dp[i-1][sum]\)
    \(dp[i][sum]+=dp[i-1][sum]\)
    复杂度最坏为\(O(nk)\)

    考场龟速\(100pts\)算法

    \(O(2^n)\)枚举去掉哪些砝码。
    然后使用\(30pts\)算法即可。
    使用快速幂和费马小定理(要求\(q\)为质数)
    \[\frac{1}{p}=p^{mod-2}(\mod q)\]
    就可以处理答案了。
    复杂度\(O(2^nnk)\),理论会炸,但\(0.64s\)过了???

    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #define ll unsigned long long
    #define re register
    #define il inline
    #define fp(i,a,b) for(re int i=a;i<=b;i++)
    #define fq(i,a,b) for(re int i=a;i>=b;i--)
    using namespace std;
    const int N=250,mod=6249433;
    int n,m,k,a[N],dp[N][N*100],pr[N],vis[N];
    ll ans;
    il ll gi() 
    {
      re ll x=0,t=1;
      re char ch=getchar();
      while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
      if(ch=='-') t=-1,ch=getchar();
      while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
      return x*t;
    }
    il void wri(re ll x)
    {
      if(x<0) putchar('-'),x=-x;
      if(x>9) wri(x/10);
      putchar(x%10+'0');
    }
    il ll check(re ll x)
    {
      while(x>=mod) x-=mod;
      return x;
    }
    il ll ksm(re ll x,re ll n)
    {
      re ll S=1,T=x;
      while(n)
    {
      if(n&1) S=check(S*T);
      T=check(T*T);
      n>>=1;
    }
      return S;
    }
    il void solve()
    {
      //fp(i,1,n) printf("%d ",pr[i]);puts("");
      fp(i,1,k) fp(j,0,n) dp[j][i]=0;
      fp(i,1,n) dp[i][a[i]]=1;
      fp(i,1,k)
    fp(j,1,n)
    if(!vis[j])
      {
    dp[j][i+a[j]]=check(dp[j][i+a[j]]+dp[pr[j]][i]);
    dp[j][i]=check(dp[j][i]+dp[pr[j]][i]);
      }
      re int ysn=n;while(vis[ysn]&&ysn) --ysn;
      ans=check(ans+dp[ysn][k]);
    }
    il void dfs(re int x,re int tot)
    {
      if(tot<0) return;
      if(x==n+1) {if(!tot) solve();return;}
      fp(i,0,1)
    if(i) vis[x]=1,pr[x+1]=pr[x],dfs(x+1,tot-1),vis[x]=0,pr[x+1]=x;
    else
      {
    if(a[x]>k) pr[x+1]=pr[x];
    dfs(x+1,tot);
      }
    }
    il ll jc(re ll s,re ll e,re int flag)
    {
      re ll pzy=1;
      fp(i,s,e) pzy=check(pzy*i);
      if(flag) pzy=ksm(pzy,mod-2);
      return pzy;
    }
    int main()
    {
      freopen("htam.in","r",stdin);
      freopen("htam.out","w",stdout);
      re int T=gi();
      while(T--)
    {
      n=gi()^ans,m=gi()^ans,k=gi()^ans;ans=0;
      if(k==0) {puts("0");continue;}
      fp(i,1,n) a[i]=gi(),pr[i]=i-1;
      dfs(1,m);
      re ll eat=1;
      if(m) eat=check(jc(1,n-m,0)*jc(m+1,n,1));
      //printf("%llu %llu\n",eat,ans);
      ans=check(ans*eat);
      wri(ans);putchar('\n');
    }
      fclose(stdin);
      fclose(stdout);
      return 0;
    }

    考场感想:

  • 编译时不开\(O2\)导致自己一直以为过不了
  • \(unsigned\ long\ long\)乘炸了都看不出来(所以一开始别用)
  • 龟速模是卡常利器

猜你喜欢

转载自www.cnblogs.com/yanshannan/p/9192907.html