@ysner
2018-06-17T17:33:35.000000Z
字数 2297
阅读 1869
DP
现有个砝码,重量分别为。太好吃了,于是吃掉了其中个砝码。
现在想知道,他在剩余的砝码中随机选取一些砝码(不能不选),正好称出的重量的方案数?
但他要我们输出的是答案 除以 吃掉砝码的方案数,并对取模。
显然可以设表示前个数中随便取数后和为的方案数。
则枚举和,可得转移方程
复杂度最坏为
枚举去掉哪些砝码。
然后使用算法即可。
使用快速幂和费马小定理(要求为质数)
#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;
}
考场感想:
枚举每个数取不取,如果取了个数,取了的数和为,就会生成个合法方案(剩下个数有个被吃了)。
复杂度,叹为观止。(其实我考场上也想到过。。。)