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)=gcd(v_j,P)\) ,那么 \(v_i,v_j\) 对答案的影响是一样的,把相同类型的一起处理,把答案乘上 \(2^{cnt}-1\) 就行了
复杂度 \(O(m^2*logP)\)

#include<bits/stdc++.h>
using namespace std;
template<class T>void gi(T &x){
    int f;char c;
    for(f=1,c=getchar();c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
    for(x=0;c<='9'&&c>='0';c=getchar())x=x*10+(c&15);x*=f;
}
const int N=1e6+10,mod=1e9+7;
int n,Q,P,num=0,a[N],m=0,c[N],f[2010][2010],bin[N],ans[N];
map<int,int>id;
inline void priwork(){
   for(int i=1;i*i<=P;i++)
        if(P%i==0){
            a[++m]=i;
            if(i*i!=P)a[++m]=P/i;
        }
    sort(a+1,a+m+1);
    for(int i=1;i<=m;i++)id[a[i]]=i;
}
inline int gcd(int x,int y){return y?gcd(y,x%y):x;}
int main(){
  freopen("pp.in","r",stdin);
  freopen("pp.out","w",stdout);
  cin>>n>>Q>>P;
  priwork();
  int x;bin[0]=1;
  for(int i=1;i<=n;i++)gi(x),c[id[gcd(x,P)]]++,bin[i]=bin[i-1]*2%mod;
  f[0][m]=1;
  for(int i=1;i<=m;i++){
      for(int j=1;j<=m;j++){
          if(c[i]){
              int p=id[gcd(a[i],a[j])];
              f[i][p]=(f[i][p]+1ll*f[i-1][j]*(bin[c[i]]-1))%mod;
          }
          f[i][j]=(f[i][j]+f[i-1][j])%mod;
      }
  }
  for(int i=1;i<=m;i++)
      for(int j=1;j<=i;j++)
          if(a[i]%a[j]==0)ans[i]=(ans[i]+f[m][j])%mod;
  while(Q--){
      gi(x);
      printf("%d\n",ans[id[gcd(x,P)]]);
  }
  return 0;
}

原文地址:https://www.cnblogs.com/Yuzao/p/8972898.html

时间: 2024-10-09 09:22:25

bzoj 5302: [Haoi2018]奇怪的背包的相关文章

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

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

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个约数有多少种

bzoj 5306 [HAOI2018] 染色

bzoj 5306 [HAOI2018] 染色 链接 推式子题 首先枚举有几种颜色选择恰好 \(s\) 次,可以得到一个式子: \[\sum _{i = 0} ^ {\min(\frac n s,m )} \frac {\binom n {i \cdot s} \binom m i (i \cdot s) ! \cdot f_i \cdot (m-i)^{n - is}} {(s!) ^ i} \] 但是,\(f_i\) 不能单纯地等于 \(w_i\), 因为会重复计算,我们不能保证当前选择的

BZOJ 2756: [SCOI2012]奇怪的游戏 [最大流 二分]

2756: [SCOI2012]奇怪的游戏 Time Limit: 40 Sec  Memory Limit: 128 MBSubmit: 3352  Solved: 919[Submit][Status][Discuss] Description Blinker最近喜欢上一个奇怪的游戏. 这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数.每次 Blinker 会选择两个相邻的格子,并使这两个数都加上 1. 现在 Blinker 想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成

BZOJ 2756: [SCOI2012]奇怪的游戏 网络流/二分

2756: [SCOI2012]奇怪的游戏 Time Limit: 40 Sec  Memory Limit: 128 MBSubmit: 1594  Solved: 396[Submit][Status][Discuss] Description Blinker最近喜欢上一个奇怪的游戏. 这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数.每次 Blinker 会选择两个相邻 的格子,并使这两个数都加上 1. 现在 Blinker 想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变

BZOJ 2756: [SCOI2012]奇怪的游戏

2756: [SCOI2012]奇怪的游戏 Time Limit: 40 Sec  Memory Limit: 128 MBSubmit: 3410  Solved: 941[Submit][Status][Discuss] Description Blinker最近喜欢上一个奇怪的游戏. 这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数.每次 Blinker 会选择两个相邻的格子,并使这两个数都加上 1. 现在 Blinker 想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成

BZOJ 3195: [Jxoi2012]奇怪的道路

3195: [Jxoi2012]奇怪的道路 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 336  Solved: 209[Submit][Status][Discuss] Description 小宇从历史书上了解到一个古老的文明.这个文明在各个方面高度发达,交通方面也不例外.考古学家已经知道,这个文明在全盛时期有n座城市,编号为1..n.m条道路连接在这些城市之间,每条道路将两个城市连接起来,使得两地的居民可以方便地来往.一对城市之间可能存在

BZOJ 1531: [POI2005]Bank notes( 背包 )

多重背包... ---------------------------------------------------------------------------- #include<bits/stdc++.h> #define rep(i, n) for(int i = 0; i < n; i++) #define clr(x, c) memset(x, c, sizeof(x)) using namespace std; const int maxn = 209, maxk =

BZOJ 2756 SCOI2012 奇怪的游戏 最大流

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2756 Description Blinker最近喜欢上一个奇怪的游戏. 这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数.每次 Blinker 会选择两个相邻的格子,并使这两个数都加上 1. 现在 Blinker 想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成同一个数则输出-1. Input 输入的第一行是一个整数T,表示输入数据有T轮游戏组成. 每轮游戏的第一行有两