[HAOI2018]奇怪的背包 (DP,数论)

时间:2023-02-16 11:53:16

[HAOI2018]奇怪的背包

[HAOI2018]奇怪的背包 (DP,数论)

[HAOI2018]奇怪的背包 (DP,数论)



$ solution: $

首先,这一道题目的描述很像完全背包,但它所说的背包总重量是在模P意义下的,所以肯定会用到数论。我们先分析一下,每一个物品可以放无数次,可以达到的背包重量其实就是所有 $ gcd(a[i],P) $ 的倍数。 这一点和天天爱跑步简直神似!因为天天爱跑步中每一个人也可以走无数步,跑到环形(就是模意义下)。

但是这道题目还可以加入多种物品,我们不难发现,如果加入i和j两种物品,它所能达到的重量其实只是在gcd中多加了一个,就是所有 $ gcd(a[i],a[j],P) $ 的倍数。这个性质在加入更多物品后依然成立。所以我们只在乎每种物品加或不加,且状态可以用P的所有约数表示(因为加入物品后能达到的重量一定是所有物品重量和P全部取gcd后的倍数)(我们只需记录这个约数即可)而我们发现P的约数个数小于3000(一般一个数的约数个数不会超过它本身的三分之一次方),所以我们可以用这个状态来完全背包:

我们定义 $ f[i][j] $ 表示已经完全背包跑完前i个物品,现在放入物品的总约数为j的方案数。然后我们发现数据范围太大了,跑不了!这怎么办? 我们发现每一个物品的贡献其实就是它的重量和P的公约数,而P的约数个数小于3000,我们可以在读入的时候就让它和P取gcd,这样会有很多物品的贡献重复(我们开个桶归类)然后每一次都按P的约数来跑完全背包。(注意要将P的约数离散化,即表示为P的第几个约数)

不过这样每一次加入某一些与P的公约数为P的第i个约数的物品时,可以取这多个物品中的某一个或多个(注意可以不选,需要加个1),所以还要乘上一个 $ (2^{物品种类数)}-1) $ (这是因为与P的公约数为P的第i个约数的物品有很多,每一个我都可以选或不选,于是有 $ 2^{物品种类数)} $ 个,然后再减去全部不选的那一种就要减一)。

$ f[i][j]=f[i-1][j]+(1+\sum_{gcd(a[k],a[i])==a[j]}{f[i-1][k]})\times (2^{tot[i]}-1) $

然后处理答案时,我们直接枚举一遍所有P的约数,然后在是这个约数倍数的答案处加上相应贡献即可!(这里有一个小优化,和我们读入一样,我们1~q以内的所有数的答案,其实就是它和P的公约数的答案!)



$ code: $

#include<iostream>
#include<cstdio>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set> #define ll long long
#define db double
#define f0 f[now^1]
#define f1 f[now]
#define rg register int using namespace std; const int mod=1e9+7; int n,m,p,tt,now;
int s[3005];
int t[3005];
int g[3005];
int a[1000005];
int pf[1000005];
int f[2][3005]; inline int qr(){
char ch;
while((ch=getchar())<'0'||ch>'9');
int res=ch^48;
while((ch=getchar())>='0'&&ch<='9')
res=res*10+(ch^48);
return res;
} inline int gcd(int x,int y){
rg z;
while(y){z=x;x=y,y=z%y;}
return x;
} inline int find(int x){
rg l=1,r=tt,mid;
while(l<=r){
mid=(l+r)>>1;
if(x<s[mid])r=mid-1;
else l=mid+1;
}return r;
} int main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
n=qr(); m=qr(); p=qr(); pf[1]=2;
for(rg i=1;i<=n;++i) a[i]=gcd(qr(),p);
for(rg i=1,j=sqrt(p);i<=j;++i) if(p%i==0)s[++tt]=i;
for(rg i=tt;i;--i) s[++tt]=p/s[i];
for(rg i=1;i<=n;++i) pf[i+1]=(pf[i]<<1)%mod,--pf[i];
for(rg i=1;i<=n;++i) ++t[find(a[i])];
for(rg i=1;i<=tt;++i){
if(!t[i])continue;else now^=1;
for(rg j=1;j<=tt;++j)f1[j]=f0[j];
for(rg j=1;j<=tt;++j){
if(!f0[j])continue;
rg gg=find(gcd(s[i],s[j]));
f1[gg]=(f1[gg]+(ll)f0[j]*pf[t[i]])%mod;
}f1[i]=(f1[i]+pf[t[i]])%mod;
}
for(rg i=1;i<=tt;++i)
for(rg j=1;j<=tt;++j)
if(s[i]%s[j]==0)g[i]=(g[i]+f1[j])%mod;
for(rg i=1;i<=m;++i)
printf("%d\n",g[find(gcd(qr(),p))]);
return 0;
}

[HAOI2018]奇怪的背包 (DP,数论)的更多相关文章

  1. BZOJ5302 &lbrack;HAOI2018&rsqb;奇怪的背包 【数论 &plus; dp】

    题目 小 CC 非常擅长背包问题,他有一个奇怪的背包,这个背包有一个参数 PP ,当他 向这个背包内放入若干个物品后,背包的重量是物品总体积对 PP 取模后的结果. 现在小 CC 有 nn 种体积不同 ...

  2. &lbrack;BZOJ5302&rsqb;&lbrack;HAOI2018&rsqb;奇怪的背包&lpar;DP&rpar;

    由裴蜀定理得,一个集合S能得到w当且仅当gcd(S+{P})|w. 于是f[i][j]表示前i个物品gcd为j的方案数,发现gcd一定是P的因数,故总复杂度$O(n\sqrt{P}\log P)$(需 ...

  3. 洛谷P4495 &lbrack;HAOI2018&rsqb;奇怪的背包(数论)

    题面 传送门 题解 好神仙的思路啊--orzyyb 因为不限次数,所以一个体积为\(V_i\)的物品可以表示出所有重量为\(\gcd(V_i,P)\)的倍数的物品,而所有物品的总和就是这些所有的\(\ ...

  4. 洛谷 P4495 &lbrack;HAOI2018&rsqb;奇怪的背包 解题报告

    P4495 [HAOI2018]奇怪的背包 题目描述 小\(C\)非常擅长背包问题,他有一个奇怪的背包,这个背包有一个参数\(P\),当他 向这个背包内放入若干个物品后,背包的重量是物品总体积对\(P ...

  5. 【BZOJ5302】&lbrack;HAOI2018&rsqb;奇怪的背包(动态规划,容斥原理)

    [BZOJ5302][HAOI2018]奇怪的背包(动态规划,容斥原理) 题面 BZOJ 洛谷 题解 为啥泥萌做法和我都不一样啊 一个重量为\(V_i\)的物品,可以放出所有\(gcd(V_i,P)\ ...

  6. BZOJ5302&colon; &lbrack;Haoi2018&rsqb;奇怪的背包

    BZOJ5302: [Haoi2018]奇怪的背包 https://lydsy.com/JudgeOnline/problem.php?id=5302 分析: 方程\(\sum\limits_{i=1 ...

  7. haoi2018奇怪的背包题解

    题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=5302 对于一个物品,设它体积为v,那么,在背包参数为p的情况下,它能达到gcd(v,p ...

  8. &lbrack;HNOI2001&rsqb; 求正整数 - 背包dp&comma;数论

    对于任意输入的正整数n,请编程求出具有n个不同因子的最小正整数m. Solution (乍一看很简单却搞了好久?我真是太菜了) 根据因子个数计算公式 若 \(m = \prod p_i^{q_i}\) ...

  9. &lbrack;HAOI2018&rsqb;奇怪的背包

    题目 暴力\(dp\)好有道理啊 于是我们来个反演吧 考虑一个体积序列\(\{v_1,v_2,...v_n\}\)能凑成\(w\)的条件 显然是 \[v_1x_1+v_2x_2+...+v_nx_n\ ...

随机推荐

  1. TableView分割线从顶端开始

    如果什么都不设置的话 分割线是从cell.textlabel处开始的 如果加上 [_myTableView setSeparatorInset:UIEdgeInsetsMake(0, 0, 0, 0) ...

  2. 堆 poj 2010

    选n个人从c个中 花费不超过f c个人的成绩和花费 求分数中位数最大 n是奇数 显然中位数是n/2+1 ~c-n/2之间的(假如存在的话) 用大顶堆维护前n/2个小的花费 求出以这个人为中位数的花费 ...

  3. idea修改jsp后不会自动编译和替换(转)

    使用IntelliJ IDEA开发JavaWeb项目时,修改了JSP后刷新浏览器无法及时显示修改后的页面? 解决办法:tomcat配置中,On frame deactivation属性选择Update ...

  4. HDU 4749-Parade Show(KMP变形)

    题意: 给出一个母串和一个模式串求母串中最多能分成最大的子串数(每个字串中的各个数字的大小关系和模式串相同) 分析: KMP变形匹配规则变一下即可,用当前数字和下个数字的差表示,大小关系方便匹配 #i ...

  5. MUH and Cube Walls

    Codeforces Round #269 (Div. 2) D:http://codeforces.com/problemset/problem/471/D 题意:给定两个序列a ,b, 如果在a中 ...

  6. linux问题: 切换用户之后变成-bash-4&period;1&dollar;

    新增用户 git 添加用户 #sudo useradd -m -s /bin/bash -g group loginname -m 创建home目录 (不加这个要手动添加目录,不然会出现No dire ...

  7. jspsmart&lpar;保存文件&rpar;&plus;poi&lpar;读取excel文件&rpar;操作excel文件

    写在前面: 项目环境:jdk1.4+weblogic 需求:能上传excel2003+2007 由于项目不仅需要上传excel2003,还要上传excel2007,故我们抛弃了jxl(只能上传exce ...

  8. SQL Server CTE 递归查询全解(转载)

    在TSQL脚本中,也能实现递归查询,SQL Server提供CTE(Common Table Expression),只需要编写少量的代码,就能实现递归查询,本文详细介绍CTE递归调用的特性和使用示例 ...

  9. 【F12】Console命令,让js调试更简单

    Console命令,让js调试更简单 一.显示信息的命令 console.log("normal"); // 用于输出普通信息 console.info("informa ...

  10. GridView 点滴

    绑定数据时.在后台给GridView添加事件 protected void grd_RowDataBound(object sender, GridViewRowEventArgs e) { //当前 ...