haoi2018奇怪的背包题解

题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=5302

对于一个物品,设它体积为v,那么,在背包参数为p的情况下,它能达到gcd(v,p)的倍数的重量

对于两个物品,设它们的体积为v1和v2,那么,在背包参数为p的情况下,他能达到gcd(v1,v2,p)的倍数的重量

对于每个物品,我们记下它的gcd(v,p),问题变为给定一个x,求有多少个v的集合,是集合内所有元素的gcd能被x整除

我们设dp[i][j]表示p的前i个约数有多少种组合是组合后的gcd为j。

接下来,我们考虑怎么转移

先给伪代码(不加取模):

for(i=1;i<=约数个数;++i)

  for(j=1;j<=约数个数;++j){

    x=gcd(第i个约数,第j个约数)dp[i][x]+=dp[i-1][j]*(2^约数i的个数-1);

    dp[i][j]+=dp[i][j-1];

  }

这种转移方式有点奇怪,是个好思路,我们平常的dp都是枚举状态,然后在寻找能转移到我们枚举的状态的状态。这个dp是先枚举已经计算完成的状态,在计算这些状态能转移到哪,更新,有点类似noip2017 提高组day1T3的拓扑排序dp写法。

上AC代码(wxy数组就是dp数组,有些细节上的处理我还没讲,可以自己实现,ps:我常数有点大(两个map)):

#include <bits/stdc++.h>
using namespace std;
const int N=2e3+10;
const int md=1e9+7;
#define _l long long
int n,q,p,ys[N];
map<int,int>reff;
map<int,int>cnt;
_l p2[N*N],wxy[N][N],an[N];
int gcd(int x,int y){return x%y==0 ? y:gcd(y,x%y);}
int main(){
    scanf("%d%d%d",&n,&q,&p);
    int i;
    for(i=1;i*i<=p;++i)if(p%i==0){
        ys[++ys[0]]=i,reff[i]=ys[0];if(i*i!=p)ys[++ys[0]]=p/i,reff[p/i]=ys[0];
    }
    for(i=1;i<=n;++i){
        int x;scanf("%d",&x);x=gcd(max(x,p),min(x,p));
        ++cnt[x];
    }
    int j;p2[0]=1;
    for(i=1;i<=n;++i)p2[i]=(p2[i-1]*2)%md;wxy[0][reff[p]]=1;
    for(i=1;i<=ys[0];++i)for(j=1;j<=ys[0];++j){
        int tmp=gcd(max(ys[i],ys[j]),min(ys[i],ys[j]));
        wxy[i][reff[tmp]]=(wxy[i][reff[tmp]]+wxy[i-1][j]*(_l)(p2[cnt[ys[i]]]-1))%md;
        wxy[i][j]=(wxy[i-1][j]+wxy[i][j])%md;
    }
    for(i=1;i<=ys[0];++i)for(j=1;j<=ys[0];++j)if(ys[i]%ys[j]==0)an[i]=(an[i]+wxy[ys[0]][j])%md;
    while(q--){
        int x;scanf("%d",&x);x=gcd(max(x,p),min(x,p));
        printf("%lld\n",an[reff[x]]);
    }
}

原文地址:https://www.cnblogs.com/david--lj/p/8974065.html

时间: 2024-11-05 22:07:02

haoi2018奇怪的背包题解的相关文章

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

[HAOI2018]奇怪的背包 \(solution:\) 首先,这一道题目的描述很像完全背包,但它所说的背包总重量是在模P意义下的,所以肯定会用到数论.我们先分析一下,每一个物品可以放无数次,可以达到的背包重量其实就是所有\(gcd(a[i],P)\)的倍数. 这一点和天天爱跑步简直神似!因为天天爱跑步中每一个人也可以走无数步,跑到环形(就是模意义下). 但是这道题目还可以加入多种物品,我们不难发现,如果加入i和j两种物品,它所能达到的重量其实只是在gcd中多加了一个,就是所有\(gcd(a[

bzoj 5302: [Haoi2018]奇怪的背包

Description Solution 首先 \(v_1,v_2,v_3...v_n,P\) 能够构成的最小数是 \(gcd(P,v_1,v_2,v_3...v_n)\) 然后 \(gcd(P,v_1,v_2,v_3...v_n)|w_i\) 则可以构成 \(w_i\) 所以我们直接背包一下就可以了,设 \(m\) 为 \(P\) 的约数个数,\(m\) 最多是 \(n^{\frac{1}{3}}\) 那么复杂度就是 \(O(n*m*logP)\) 容易发现如果 \(gcd(v_i,P)=gc

POJ 2392 Space Elevator 背包题解

多重背包,本题不需要二分优化.相对简单点.因为重复数十分小,小于10: 而增加一个限制每种材料的高度做法,如果使用逆向填表,那么只需要从这个高度往小递归填表就可以了. 还有就是注意要排序,以限制高度为标准从小到大排序,否则答案错误的. #include <stdio.h> #include <string.h> #include <algorithm> using std::sort; const int MAX_K = 401; const int MAX_H = 4

POJ 3211 Washing Clothes 背包题解

本题是背包问题,但是需要转化成背包的. 因为是两个人洗衣服,那么就是说一个人只需要洗一半就可以了,因为不能两个人同时洗一件衣服,所以就成了01背包问题了. 思路: 1 计算洗完同一颜色的衣服需要的总时间totTime 2 利用动态规划背包法求这些衣服能在那些时间点完成 3 求比(totTime+1)/2大的最小时间点 4 得到洗一种颜色衣服的时间,那么继续求下洗一种颜色衣服的时间 5 最后加起来就是答案了. 这个是算法问题. 剩下来就是考编程功力了,因为给出的数据,需要我们自己把衣服分类,分类之

POJ 3624 Charm Bracelet 背包题解

最简单的背包问题了,本题应该除了背包就一个考点了:不能开二维数组.我没开过二维,不过看数据是不可以的.太大了. 做法有两种改进省内存DP: 1 所谓的滚动数组 2 逆向填表 很久没做背包DP,突然觉得这种背包问题很简单了. 下面给出两种解法: 1 calBag()是滚动数组 2 calBag2()是逆向填表 #pragma once #include <stdio.h> #include <stdlib.h> #include <vector> using namesp

POJ 1276 Cash Machine 背包题解

典型的多重背包的应用题解. 可以使用二进制优化,也可以使用记录当前物品的方法解,速度更加快. const int MAX_CASH = 100001; const int MAX_N = 11; int tbl[MAX_CASH], nums[MAX_N], bills[MAX_N], cash, n; int bag() { if (cash <= 0 || n <= 0) return 0; memset(tbl, 0, sizeof(int) * (cash+1)); for (int

HDU 1203 I NEED A OFFER! 01背包题解

本题题目居然写错了也没改正,囧,应该是AN OFFER! 题解就是dp加上概率论求解. 因为需要求最少有一间学校录取的概率,那么就可以使用逆向思维,求没有一间学校录取的概率.基本的概率论思维,不过如果久了没做概率论还是会有点麻烦的. 然后就是标准的01背包求解了: #include <stdio.h> #include <vector> #include <string.h> #include <algorithm> #include <iostrea

【CJWYH】RHL的背包题解(多重背包)

题面 [问题描述] CJ中学组织学生出去春游,作为学神的RHL自然不会放过这一大好时机,他有n种物品,第i件物品有c[i]个,每个体积为v[i],价值为w[i],RHL现在有一个体积为V的背包,他想让他带的东西价值之和最大,且体积之和不超过V,你能帮帮他吗?注意物体不能分割. [输入] 输入文件名为bag.in,分为若干行.第一行包含两个正整数n,V. 第二行到第n+1行分别描述第i种物品的数量c[i],体积v[i],价值w[i] [输出] 输出文件名为bag.out,一行输出一个整数,表示最大

【基础练习】codevs2833 奇怪的梦境题解

题目描述 Description Aiden陷入了一个奇怪的梦境:他被困在一个小房子中,墙上有很多按钮,还有一个屏幕,上面显示了一些信息.屏幕上说,要将所有按钮都按下才能出去,而又给出了一些信息,说明了某个按钮只能在另一个按钮按下之后才能按下,而没有被提及的按钮则可以在任何时候按下.可是Aiden发现屏幕上所给信息似乎有矛盾,请你来帮忙判断. 输入描述 Input Description 第一行,两个数N,M,表示有编号为1...N这N个按钮,屏幕上有M条信息. 接下来的M行,每行两个数ai,b