20180516模擬賽T3——bag
阿新 • • 發佈:2018-05-16
.com CP 圖片 組合 多少 github ++ int 證明
題解
Cqz大佬在代碼上的註釋:
前i個物品,做成體積為j的東西,有多少種方案數
後i個物品,做成體積為j的東西,有多少種方案書(大佬打錯了)
兩個DP數組合並。 做不到?
其實就是把中間那段切斷,然後把左右兩邊合並。
貼一段代碼吧:
Rep(i,1,n)
{
Dep(j,m,v[i])
f[i][j] = (f[i-1][j] + f[i-1][j - v[i]])%Mod;
Dep(j,v[i]-1,0)
f[i][j] = f[i-1][j];
}
g[n+1][0] = 1;
Dep(i,n,1)
{
Dep(j,m,v[i])
g[i][j] = (g[i+1 ][j] + g[i+1][j - v[i]])%Mod;
Dep(j,v[i]-1,0)
g[i][j] = g[i+1][j];
}
Rep(i,1,n)
{
ll ans = 0;
Rep(j,0,m)
ans = (ans + (1ll * f[i-1][j] * g[i+1][m-j] % Mod)) % Mod;//註意合並階段一定要開long long
writeln(ans);
}
zd大佬&sxd大佬的做法
先跑一遍01背包,要刪除一段時,倒著跑一遍。請教了兩位大佬,然而好像都無法證明。但在感性上應該是可以理解的(極其具有對稱性)。
這個的代碼就簡單多了:
dp[0] = 1; for(int i = 1; i <= n; ++i) for(int j = m; j >= v[i]; --j) dp[j] = (dp[j]+dp[j-v[i]])%mod; for(int i = 1; i <= n; ++i) { for(int j = 0; j <= m; ++j) f[j] = dp[j]; for(int j = v[i]; j <= m; ++j) f[j] = ((f[j]-f[j-v[i]])%mod+mod)%mod; writeln(f[m]); }
聽說yyh大佬有一種容斥做法,然而我看不懂啊……只好直接貼代碼了:
f[0]=1;
for(LL i=1; i<=n; i++)
{
a[i]=read();
for(LL j=m; j>=a[i]; j--)
(f[j]+=f[j-a[i]])%=md;
}
for(LL i=1; i<=n; i++)
{
ans=f[m];
for(LL j=1; j*a[i]<=m; j++)
{
if(j&1)
(ans-=f[m-j*a[i]])%=md;
else
(ans+=f[m-j*a[i]])%=md;
}
printf("%lld\n",(ans%md+md)%md);
}
20180516模擬賽T3——bag