第一眼生成函数。四个等比数列形式的多项式相乘,可以化成四个分式。其中分母部分是固定的,可以多项式求逆预处理出来。而分子部分由于项数很少,询问时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. blob 对象 实现分片上传 及 显示进度条

    blob对象介绍 一个 Blob对象表示一个不可变的, 原始数据的类似文件对象.Blob表示的数据不一定是一个JavaScript原生格式 blob对象本质上是js中的一个对象,里面可以储存大量的二进 ...

  2. 开发工具|给你的项目买份保险:Python虚拟环境

    读完需要 9 分钟 1. 什么是虚拟环境? 虚拟环境的意义,就如同 虚拟机 一样,它可以实现不同环境中Python依赖包相互独立,互不干扰.这在一定程度的意义上,给了我们的项目一份很有力的保障.在这里 ...

  3. POJ3301 Texas Trip 计算几何、随机化贪心

    传送门--Vjudge 三分写法似乎有问题,可以去Udebug上看Morass的\(666\)个测试点的数据,我的乱搞有很多比正解答案小,但还是能在SPOJ和POJ过,可见数据之水. 可以对正方形的角 ...

  4. React-本地状态(state)

    在类组件中添加本地状态(state): 1.创建一个继承自 React.Component 类的 ES6 class 同名类: 2.添加一个 类构造函数(class constructor) 初始化 ...

  5. React-将元素渲染到 DOM 中

    首先我们在一个 HTML 页面中添加一个 id="root" 的 <div>,在此 div 中的所有内容都将由 React DOM 来管理,所以我们将其称之为 “根” ...

  6. 获取2个集合List<T>的共同元素

    获取2个集合List<T>的共同元素,循环2个集合,然后比对. class Bj { public void GetIntersect() { , , , , , , }; , , , , ...

  7. c# WPF 设置窗口一直在其中窗口后面/底层窗口

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.W ...

  8. RNN介绍,较易懂

    人类并不是每时每刻都从一片空白的大脑开始他们的思考.在你阅读这篇文章时候,你都是基于自己已经拥有的对先前所见词的理解来推断当前词的真实含义.我们不会将所有的东西都全部丢弃,然后用空白的大脑进行思考.我 ...

  9. RabbitMQ 优先级队列-为队列赋权

    RabbitMQ 消息收发是按顺序收发,一般情况下是先收到的消息先处理,即可以实现先进先出的消息处理.但如果消息者宕机或其他原因,导致消息接收以后,未确认,那么消息会重新Requeue到队列中,就打破 ...

  10. ABP module-zero +AdminLTE+Bootstrap Table+jQuery权限管理系统第十四节--后台工作者HangFire与ABP框架Abp.Hangfire及扩展

    返回总目录:ABP+AdminLTE+Bootstrap Table权限管理系统一期 HangFire与Quartz.NET相比主要是HangFire的内置提供集成化的控制台,方便后台查看及监控,对于 ...