4390. 【GDOI2016模拟3.16】图计数 (Standard IO)
Description
Input
Output
Solutions
题目一开始觉得简单,后来讨论才发现我连题都读错了啊!!!
实际上这就是一个完全背包问题,大小为n,物品 为1,2,......,n重量小于O(√n)的物品只有O(√n)种,
重量大于O(√n)的物品每种只会用O(√n)个 分这两种情况分别考虑,最后合并答案。
前O(√n)个小物品只需套用普通的完全背包求解即可
后面的大物品考虑下面这种动态规划:
设f[i][j]表示i个物品重量和为j的方案数,
那么f[i][j]=f[i-1][j−K]+f[i][j−i],
这个式子第一项表示在后面新增了一个重量为K的物品,第二项表示把所有物品的重量新增1,
因为用的物品最多不会超过O(√n)个,所以状态数不超过O(n√n),两种DP时间都不会超过O(n√n)
这题用c++写的选手有优势,pascal的选手不优化可能会TLE
代码
1 var 2 n,m,nm,p:longint; 3 ans:int64; 4 f,sum:array [0..200001] of int64; 5 dp:array [0..1,0..200001] of int64; 6 procedure dp2; 7 var 8 i,j,kk:longint; 9 begin 10 dp[0,0]:=1; sum[0]:=1; kk:=1; f[0]:=1; 11 for i:=1 to nm do 12 begin 13 fillchar(dp[kk],sizeof(dp[kk]),0); 14 for j:=1 to n do 15 begin 16 if j-nm-1>=0 then dp[kk,j]:=dp[kk,j]+dp[kk xor 1,j-nm-1]; 17 if j-i>=0 then 18 begin 19 dp[kk,j]:=dp[kk,j]+dp[kk,j-i]; 20 f[j]:=f[j]+f[j-i]; 21 end; 22 sum[j]:=sum[j]+dp[kk,j]; 23 if (dp[kk,j]>2000000000) then dp[kk,j]:=dp[kk,j] mod p; 24 if (sum[j]>2000000000) then sum[j]:=sum[j] mod p; 25 if (f[j]>2000000000) then f[j]:=f[j] mod p; 26 end; 27 kk:=kk xor 1; 28 end; 29 end; 30 31 function mi(a,b:int64):int64; 32 var 33 c:int64; 34 begin 35 if b=1 then exit(a); 36 c:=mi(a,b div 2); 37 c:=(c*c) mod p; 38 if b mod 2=1 then c:=(c*a) mod p; 39 exit(c); 40 end; 41 42 procedure main; 43 var 44 i:longint; 45 begin 46 ans:=0; 47 for i:=0 to n do 48 ans:=(ans+(f[i]*sum[n-i]) mod p) mod p; 49 p:=p+1; 50 writeln(mi(m,ans)); 51 end; 52 53 begin 54 readln(n,m); 55 nm:=trunc(sqrt(n))+1; 56 p:=999999598; 57 dp2; 58 main; 59 end.