BZOJ1042 HAOI2008硬币购物(任意模数NTT+多项式求逆+生成函数/容斥原理+动态规划)
第一眼生成函数。四个等比数列形式的多项式相乘,可以化成四个分式。其中分母部分是固定的,可以多项式求逆预处理出来。而分子部分由于项数很少,询问时2^4算一下贡献就好了。这个思路比较直观。只是常数巨大,以及需要敲一发类似任意模数ntt的东西来避免爆精度。成功以这种做法拿下luogu倒数rank1,至于bzoj不指望能过了。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iomanip>
using namespace std;
int read()
{
int x=,f=;char c=getchar();
while (c<''||c>'') {if (c=='-') f=-;c=getchar();}
while (c>=''&&c<='') x=(x<<)+(x<<)+(c^),c=getchar();
return x*f;
}
#define N 550000
#define T 100000
#define P1 998244353
#define P2 1004535809
int r[N],c1,c2,c3,c4,tot,d1,d2,d3,d4,s,t;
int a[N],b[N],c[N],e[][N];
long long f[N];
void inc(int &x,int P){x++;if (x>=P) x-=P;}
void dec(int &x,int P){x--;if (x<) x+=P;}
int ksm(int a,int k,int P)
{
if (a==) return ;
if (k==) return ;
int tmp=ksm(a,k>>,P);
if (k&) return 1ll*tmp*tmp%P*a%P;
else return 1ll*tmp*tmp%P;
}
long long ksc(long long a,long long b,long long P)
{
long long t=a*b-(long long)((long double)a*b/P+0.5)*P;
return t<?t+P:t;
}
void DFT(int n,int *a,int p,int P)
{
for (int i=;i<n;i++) if (i<r[i]) swap(a[i],a[r[i]]);
for (register int i=;i<=n;i<<=)
{
int wn=ksm(p,(P-)/i,P);
for (register int j=;j<n;j+=i)
{
int w=;
for (register int k=j;k<j+(i>>);k++,w=1ll*w*wn%P)
{
int x=a[k],y=1ll*w*a[k+(i>>)]%P;
a[k]=(x+y)%P,a[k+(i>>)]=(x-y+P)%P;
}
}
}
}
void mul(int n,int *a,int *b,int P,int inv3)
{
DFT(n,a,,P),DFT(n,b,,P);
for (int i=;i<n;i++) a[i]=1ll*a[i]*(P+-1ll*a[i]*b[i]%P)%P;
DFT(n,a,inv3,P);
int inv=ksm(n,P-,P);
for (int i=;i<n;i++) a[i]=1ll*a[i]*inv%P;
}
void solve(int P,int inv3,int op)
{
memset(a,,sizeof(a));
memset(b,,sizeof(b));
memset(c,,sizeof(c));
if (c1+c2+c3+c4<=T) inc(a[c1+c2+c3+c4],P);
if (c1+c2+c3<=T) dec(a[c1+c2+c3],P);
if (c1+c2+c4<=T) dec(a[c1+c2+c4],P);
if (c4+c2+c3<=T) dec(a[c4+c2+c3],P);
if (c1+c4+c3<=T) dec(a[c1+c4+c3],P);
if (c1+c2<=T) inc(a[c1+c2],P);
if (c1+c3<=T) inc(a[c1+c3],P);
if (c1+c4<=T) inc(a[c1+c4],P);
if (c2+c3<=T) inc(a[c2+c3],P);
if (c4+c2<=T) inc(a[c4+c2],P);
if (c3+c4<=T) inc(a[c3+c4],P);
dec(a[c1],P);dec(a[c2],P);dec(a[c3],P);dec(a[c4],P);
inc(a[],P);
t=;b[]=;
while (t<=T)
{
t<<=;
for (int i=;i<t;i++) c[i]=a[i];
for (int i=;i<(t<<);i++) r[i]=(r[i>>]>>)|(i&)*t;
mul(t<<,b,c,P,inv3);
for (int i=t;i<(t<<);i++) b[i]=;
}
memcpy(e[op],b,sizeof(e[op]));
}
void crt()
{
long long P=1ll*P1*P2,inv1=ksm(P2%P1,P1-,P1),inv2=ksm(P1%P2,P2-,P2);
for (int i=;i<=T;i++)
f[i]=(ksc(1ll*e[][i]*P2%P,inv1,P)+ksc(1ll*e[][i]*P1%P,inv2,P))%P;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("bzoj1042.in","r",stdin);
freopen("bzoj1042.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
c1=read(),c2=read(),c3=read(),c4=read(),tot=read();
solve(P1,,);
solve(P2,,);
crt();
while (tot--)
{
d1=read(),d2=read(),d3=read(),d4=read(),s=read();
d1=min(1ll*s+,1ll*(d1+)*c1);
d2=min(1ll*s+,1ll*(d2+)*c2);
d3=min(1ll*s+,1ll*(d3+)*c3);
d4=min(1ll*s+,1ll*(d4+)*c4);
long long ans=f[s];
if (d1+d2+d3+d4<=s) ans+=f[s-(d1+d2+d3+d4)];
if (d1+d2+d3<=s) ans-=f[s-(d1+d2+d3)];
if (d1+d2+d4<=s) ans-=f[s-(d1+d2+d4)];
if (d4+d2+d3<=s) ans-=f[s-(d4+d2+d3)];
if (d1+d4+d3<=s) ans-=f[s-(d1+d4+d3)];
if (d1+d2<=s) ans+=f[s-(d1+d2)];
if (d1+d3<=s) ans+=f[s-(d1+d3)];
if (d1+d4<=s) ans+=f[s-(d1+d4)];
if (d2+d3<=s) ans+=f[s-(d2+d3)];
if (d4+d2<=s) ans+=f[s-(d4+d2)];
if (d3+d4<=s) ans+=f[s-(d3+d4)];
if (d1<=s) ans-=f[s-d1];
if (d2<=s) ans-=f[s-d2];
if (d3<=s) ans-=f[s-d3];
if (d4<=s) ans-=f[s-d4];
printf(LL,ans);
}
return ;
}
还有一种更优秀的做法。考虑如果硬币没有个数限制的话,就是一个完全背包。添加限制可以想到容斥。我们枚举有哪几种硬币超过了个数限制,就可以容斥斥斥容容容斥把多重背包转化成完全背包了。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iomanip>
using namespace std;
int read()
{
int x=,f=;char c=getchar();
while (c<''||c>'') {if (c=='-') f=-;c=getchar();}
while (c>=''&&c<='') x=(x<<)+(x<<)+(c^),c=getchar();
return x*f;
}
#define N 100010
#define ll long long
int c[],t,d[],s;
ll f[N],ans;
int calc(int k,int x){if (k<x) return ;else return f[k-x];}
void dfs(int k,int sum,ll tot)
{
if (tot>s) return;
if (k==) {ans+=((sum&)?-:)*f[s-tot];return;}
dfs(k+,sum+,tot+1ll*(d[k]+)*c[k]);
dfs(k+,sum,tot);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("bzoj1042.in","r",stdin);
freopen("bzoj1042.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
for (int i=;i<;i++) c[i]=read();
t=read();
f[]=;
for (int i=;i<;i++)
for (int j=c[i];j<=N-;j++)
f[j]+=f[j-c[i]];
while (t--)
{
for (int i=;i<;i++) d[i]=read();
s=read();
ans=;
dfs(,,);
printf(LL,ans);
}
return ;
}
仔细考虑一下会发现两个做法本质上其实是一样的。分子部分所乘的多项式就是一个容斥的过程,而求逆所得的结果就是完全背包。
BZOJ1042 HAOI2008硬币购物(任意模数NTT+多项式求逆+生成函数/容斥原理+动态规划)的更多相关文章
- 【bzoj3456】城市规划 容斥原理+NTT+多项式求逆
题目描述 求出n个点的简单(无重边无自环)无向连通图数目mod 1004535809(479 * 2 ^ 21 + 1). 输入 仅一行一个整数n(<=130000) 输出 仅一行一个整数, 为 ...
- BZOJ1042 [HAOI2008]硬币购物 【完全背包 + 容斥】
1042: [HAOI2008]硬币购物 Time Limit: 10 Sec Memory Limit: 162 MB Submit: 2924 Solved: 1802 [Submit][St ...
- 【BZOJ 3456】 3456: 城市规划 (NTT+多项式求逆)
3456: 城市规划 Time Limit: 40 Sec Memory Limit: 256 MBSubmit: 658 Solved: 364 Description 刚刚解决完电力网络的问题 ...
- NTT+多项式求逆+多项式开方(BZOJ3625)
定义多项式$h(x)$的每一项系数$h_i$,为i在c[1]~c[n]中的出现次数. 定义多项式$f(x)$的每一项系数$f_i$,为权值为i的方案数. 通过简单的分析我们可以发现:$f(x)=\fr ...
- BZOJ 4555 [Tjoi2016&Heoi2016]求和 ——分治 NTT 多项式求逆
不想多说了,看网上的题解吧,我大概说下思路. 首先考察Stirling的意义,然后求出递推式,变成卷积的形式. 然后发现贡献是一定的,我们可以分治+NTT. 也可以直接求逆(我不会啊啊啊啊啊) #in ...
- BZOJ 3456 城市规划 ( NTT + 多项式求逆 )
题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=3456 题意: 求出\(n\)个点的简单(无重边无自环)无向连通图的个数.(\(n< ...
- [BZOJ3456]城市规划:DP+NTT+多项式求逆
写在前面的话 昨天听吕老板讲课,数数题感觉十分的神仙. 于是,ErkkiErkko这个小蒟蒻也要去学数数题了. 分析 Miskcoo orz 带标号无向连通图计数. \(f(x)\)表示\(x\)个点 ...
- P4233-射命丸文的笔记【NTT,多项式求逆】
正题 题目链接:https://www.luogu.com.cn/problem/P4233 题目大意 随机选择一条有哈密顿回路的\(n\)个点的竞赛图,求选出图的哈密顿回路的期望个数. 对于每个\( ...
- BZOJ1042 [HAOI2008]硬币购物 完全背包 容斥原理
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1042 题目概括 硬币购物一共有4种硬币.面值分别为c1,c2,c3,c4.某人去商店买东西,去了t ...
随机推荐
- jmeter(十八)关联之XPath Extractor
之前的博客,有介绍jmeter如何对请求进行关联的一种常见用法,即:后置处理器中的正则表达式提取器,下面介绍另一种关联方法,XPath Extractor! 所谓关联,从业务角度讲,即:某些操作步骤与 ...
- npm安装cnpm报错
1.持久使用 npm config set registry https://registry.npm.taobao.org // 配置后可通过下面方式来验证是否成功npm config get re ...
- python descriptor 详解
descriptor简介 在python中,如果一个新式类定义了__get__, __set__, __delete__方法中的一个或者多个,那么称之为descriptor.descriptor有分为 ...
- Oracle ORA-14102: 只能指定一个 LOGGING 或 NOLOGGING 子句
oracle 11g ,在通过命令impdp向一个数据库用户导入数据时,出现错误: ORA-14102: 只能指定一个 LOGGING 或 NOLOGGING 子句 造成此问题的原因是:当导入的表里没 ...
- Luogu3162 CQOI2012 组装 贪心
传送门 如果提供每一种零件的生产车间固定了,那么总时间\(t\)与组装车间的位置\(x\)的关系就是 \(t = \sum (x-a_i)^2 = nx^2-2\sum a_ix + \sum a_i ...
- Luogu2469 SDOI2010 星际竞速 费用流
传送门 发现它的本质是求一个费用最小的路径覆盖 最小路径覆盖是网络流23题中的一个比较典型的模型 所以考虑相似的建边 因为每一个点要恰好经过一次,是一个有上下界的网络流,故拆点,星球\(i\)拆成\( ...
- 一头雾水的"Follow The Pointer"
原文:一头雾水的"Follow The Pointer" 一头雾水的"Follow The Pointer" ...
- el取bean 对象属性规则
1,去map 根据map key 2,取bean中属性 根据get方法,getaaa() getAaa() ${xxx.aaa}可以取到此方法. ${xxx.Aaa}报错
- Ionic App ActionSheet布局问题
使用 $ionicActionSheet的时候界面不像Ihpnoe手机那样显示的整齐,但是在电脑浏览器里面却是好的,原因还是Ionic自带css的问题,网上的答案是ionic.min.css/ioni ...
- 更换pip源到国内镜像
1.pip国内的一些镜像 阿里云 https://mirrors.aliyun.com/pypi/simple/ 中国科技大学 https://pypi.mirrors.ustc.edu.cn/ ...