十二省聯考2019 皮配(揹包神題)

2021-09-26 20:58:23 字數 4167 閱讀 9696

我zxyoi學oi兩年,什麼場面沒見過。

這場面我真沒見過。

真正的dp好題,想清楚之後完全不難寫。

雖然寫完再去看覺得窒息得一批。

準確來說,這道題的考察內容就是揹包計數。

是的,只有揹包計數,除了乘法原理甚至可以說沒有任何組合數學內容。

純粹到極致的揹包dp神仙題。

設s

ss為s

ss之和,即總人數。

首先考慮低分暴力(因為正解裡面會用到)。

直接維護三位導師旗下的學員人數進行轉移,顯然第四位導師可以直接算出來,轉移複雜度o(n

m3

)o(nm^3)

o(nm3)

發現其實可以直接分陣營和派系維護,即我們只維護藍陣營和r派系的人數即可,複雜度o(n

m2

)o(nm^2)

o(nm2)

考慮k=

0k=0

k=0的情況。

我們重新理一遍題意,發現這種情況下陣營和派系的選擇互不影響。

分別揹包dp,設f[i

]f[i]

f[i]

表示藍陣營中人數為i

ii的方案數,g[i

]g[i]

g[i]

表示鴨派系中人數為j

jj的方案數。

則由乘法原理,答案為∑i=

s−c1

c0∑j

=s−d

1d0f

[i]g

[j

]\sum\limits_^\sum\limits_^f[i]g[j]

i=s−c1

​∑c0

​​j=

s−d1

​∑d0

​​f[

i]g[

j]接下來考慮存在某些學校討厭某個導師。

發現對於沒有任何乙個學校討厭某個導師的城市,和沒有討厭的導師的學校,這部分計數可以直接套用上面的做法。

然後對於有限制的,可以直接套用暴力解法。

列舉有限制的學校分在藍陣營,鴨派系的有多少人,剩下的部分直接乘法原理計算即可,需要維護一下上面揹包結果的字首和。

揹包過程中需要比較精細的上界優化,不然複雜度假的。

單組資料複雜度o((

c+n)

m+k2

sm

)o((c+n)m+k^2sm)

o((c+n

)m+k

2sm)

**:

#include

#define ll long long

#define re register

#define gc get_char

#define cs const

namespace io

template

<

typename t>

inline t get()

inline

intgi()

}using

namespace io;

using std::cerr;

using std::cout;

cs int mod=

998244353

;inline

intadd

(int a,

int b)

inline

void

inc(

int&a,

int b)

cs int n=

1e3+

7,m=

2.5e3+7

;bool ban[n]

;int sum[n]

,hate[n]

,b[n]

,s[n]

;int f[m]

,f[m]

[m];

int g[m]

,g[m]

[m];

std::vector<

int> c[n]

;int n,c,c0,c1,d0,d1,tot,k;

inline

void

solve()

k=gi()

;while

(k--

)int lim=

0,tmp=0;

memset

(f,0

,sizeof

(int)*

(c0+1)

);f[0]

=1;lim=0;

for(

int re i=

1;i<=c;

++i)if(

!ban[i]

&&sum[i]

)for

(int re j=lim=std::

min(lim+

(tmp=sum[i]

),c0)

;j>=tmp;

--j)

inc(f[j]

,f[j-tmp]);

for(

int re i=

1;i<=c0;

++i)

inc(f[i]

,f[i-1]

);memset

(g,0

,sizeof

(int)*

(d0+1)

);g[0]

=1;lim=0;

for(

int re i=

1;i<=n;

++i)if(

!~hate[i]

)for

(int re j=lim=std::

min(lim+

(tmp=s[i]

),d0)

;j>=tmp;

--j)

inc(g[j]

,g[j-tmp]);

for(

int re i=

1;i<=d0;

++i)

inc(g[i]

,g[i-1]

);int cs=

0,ss=

0;f[0]

[0]=

1;for(

int re ct=

1;ct<=c;

++ct)

if(ban[ct])if

(hate[a]

>=2)

for(

int re i=

0;i<=cs;

++i)

for(

int re j=ss;j>=t;

--j)

inc(f[i]

[j],f[i]

[j-t]);

if(hate[a]==3

)for

(int re i=

0;i<=cs;

++i)

if(hate[a]

<=1)

for(

int re i=

0;i<=cs;

++i)

for(

int re j=ss;j>=t;

--j)

inc(g[i]

[j],g[i]

[j-t]);

}for

(int re j=

0,tmp=sum[ct]

;j<=ss;

++j)

for(

int re i=

0;i<=cs;

++i)

for(

int re j=

0;j<=ss;

++j)

inc(f[i]

[j],g[i]

[j]);}

int res=0;

for(

int re i=

0;i<=cs;

++i)

for(

int re j=

0;j<=ss;

++j)

cout<"\n"

;for

(int re i=

0;i<=cs;

++i)

memset

(f[i],0

,sizeof

(int)*

(ss+1)

),memset

(g[i],0

,sizeof

(int)*

(ss+1)

);}signed

main()

十二省聯考2019 皮配

十二省聯考2019 皮配 巧妙運用 獨立 的性質,對於 不獨立 的進行暴力處理,再合併 1.dp i j k 前i個城市,藍有j,鴨有k個方案數 2.無限制時,城市的陣營和學校的派系獨立,直接dp出城市選擇方案,派系選擇方案,直接乘起來。顯然唯一分配且一一對應 有限制,只有30個 考慮對於有限制的城...

十二省聯考 2019 皮配

你有 n 個集合,每個集合裡有一些非負整數。有 4 個桶,現在要把每個非負整數放入其中 1 個桶。限制條件 1.同一集合的所有數要麼都放入第 1,2 個桶,要麼都放入第 3,4 個桶。2.有 k 個數有特殊要求 不能放入前 2 個桶或不能放入後 2 個桶。3.設 cnt i 為第 i 個桶放的數的總...

十二省聯考2019 皮配

題目 十二省聯考2019 皮配 設s等於各學校人數之和。首先,有乙個很簡單的 o nm 2 的 dp 記錄當前考慮到哪所學校,以及藍陣營人數a,鴨派系人數b,最後,只要滿足 s c1 a c0 且 s d1 b d0 這個方案就是合法的。寫上這個就有50分了。注意到還有20分滿足 k 0 即沒有限制...