第一眼生成函数。四个等比数列形式的多项式相乘,可以化成四个分式。其中分母部分是固定的,可以多项式求逆预处理出来。而分子部分由于项数很少,询问时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+多项式求逆+生成函数/容斥原理+动态规划)的更多相关文章

  1. 【bzoj3456】城市规划 容斥原理+NTT+多项式求逆

    题目描述 求出n个点的简单(无重边无自环)无向连通图数目mod 1004535809(479 * 2 ^ 21 + 1). 输入 仅一行一个整数n(<=130000) 输出 仅一行一个整数, 为 ...

  2. BZOJ1042 [HAOI2008]硬币购物 【完全背包 + 容斥】

    1042: [HAOI2008]硬币购物 Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 2924  Solved: 1802 [Submit][St ...

  3. 【BZOJ 3456】 3456: 城市规划 (NTT+多项式求逆)

    3456: 城市规划 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 658  Solved: 364 Description 刚刚解决完电力网络的问题 ...

  4. NTT+多项式求逆+多项式开方(BZOJ3625)

    定义多项式$h(x)$的每一项系数$h_i$,为i在c[1]~c[n]中的出现次数. 定义多项式$f(x)$的每一项系数$f_i$,为权值为i的方案数. 通过简单的分析我们可以发现:$f(x)=\fr ...

  5. BZOJ 4555 [Tjoi2016&Heoi2016]求和 ——分治 NTT 多项式求逆

    不想多说了,看网上的题解吧,我大概说下思路. 首先考察Stirling的意义,然后求出递推式,变成卷积的形式. 然后发现贡献是一定的,我们可以分治+NTT. 也可以直接求逆(我不会啊啊啊啊啊) #in ...

  6. BZOJ 3456 城市规划 ( NTT + 多项式求逆 )

    题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=3456 题意: 求出\(n\)个点的简单(无重边无自环)无向连通图的个数.(\(n< ...

  7. [BZOJ3456]城市规划:DP+NTT+多项式求逆

    写在前面的话 昨天听吕老板讲课,数数题感觉十分的神仙. 于是,ErkkiErkko这个小蒟蒻也要去学数数题了. 分析 Miskcoo orz 带标号无向连通图计数. \(f(x)\)表示\(x\)个点 ...

  8. P4233-射命丸文的笔记【NTT,多项式求逆】

    正题 题目链接:https://www.luogu.com.cn/problem/P4233 题目大意 随机选择一条有哈密顿回路的\(n\)个点的竞赛图,求选出图的哈密顿回路的期望个数. 对于每个\( ...

  9. BZOJ1042 [HAOI2008]硬币购物 完全背包 容斥原理

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1042 题目概括 硬币购物一共有4种硬币.面值分别为c1,c2,c3,c4.某人去商店买东西,去了t ...

随机推荐

  1. helm 部署 使用 记录

    0.概念:Helm作为一个包管理工具, 它把Kubernetes资源(比如deployments.services或 ingress等) 打包到一个chart中,方便我们将其chart保存到chart ...

  2. 如何扩展32位EXE程序的使用内存

    1 运行Visual studio的命令行,执行下面命令:editbin /LARGEADDRESSAWARE “C:\Program Files\Skyline\TerraExplorer Pro\ ...

  3. eclipse中使用svn提交,更新代码。

    在新公司工作,版本管理工具变成了svn,之前一直用git作为版本管理,用的编辑IDE是IntelliJIDEA,在这个编辑器下工作,还是很方便的,但是现在使用eclipse和svn.有点不习惯,但还是 ...

  4. EF Core中DbContext可以被Dispose多次

    我们知道,在EF Core中DbContext用完后要记得调用Dispose方法释放资源.但是其实DbContext可以多次调用Dispose方法,虽然只有第一次Dispose会起作用,但是DbCon ...

  5. Verilog设计异步FIFO

    转自http://ninghechuan.com 异步FIFO有两个异步时钟,一个端口写入数据,一个端口读出数据.通常被用于数据的跨时钟域的传输. 同步FIFO的设计.一个时钟控制一个计数器,计数器增 ...

  6. Telephone Phrases

    There are some common phrases and sentences you can use when speaking on the telephone. The informal ...

  7. zabbix邮件报警功能的验证

    zabbix里面设置了很多监控项,有很多重要的监控预警,必须保证zabbix邮件报警功能正常,以确保那些告警信息能及时发送到运维人员的邮箱里. 所以需要每天8:30发一封确认zabbix邮件报警功能正 ...

  8. og标签对SEO的作用及用法

    meta property=og标签对SEO的作用及用法,如果你仔细观察会发现本站点<head>代码中有一段:"property="og:image"这段代码 ...

  9. #科委外文文献发现系统——导出word模板1.0

    ps:该篇文档由实验室ljg提供. Crowdsourcing 一.             技术简介 Crowdsourcing, a modern business term coined in ...

  10. 面向对象课程 - 寒假第四次作业 - C++计算器项目计算部分

    C++计算器项目计算部分 零.项目源文件地址 地址:Calculator 2.0 一.项目信息相关 项 目 : Calculator 版 本 : 2 . 0 日 期 : 2016 . 4 . 14 实 ...