1. 程式人生 > >20180516模擬賽T3——bag

20180516模擬賽T3——bag

.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