【训练题66:状压暴力 | 子集dp】Greater Integer, Better LCM | 2021牛客暑期多校训练营5

题意

  • Greater Integer, Better LCM | 2021牛客暑期多校训练营5
    给你 a , b , c a,b,c a,b,c ,你需要找到一对 x , y x,y x,y ,满足:
    l c m ( a + x , b + y ) = c lcm(a+x,b+y)=c lcm(a+x,b+y)=c
    你需要输出最小的满足的 x + y x+y x+y 的值
    注意,这里给出的 c c c 的形式为 ∏ i = 1 n p i q i \prod_{i=1}^n p_i^{q_i} i=1npiqi
  • 1 ≤ n ≤ 18 1\le n \le 18 1n18
    1 ≤ a , b , c ≤ 1 0 32 1\le a,b,c\le 10^{32} 1a,b,c1032
    ∑ q i ≤ 18 \sum q_i\le 18 qi18

思路

  • 首先注意一下, a , b , c a,b,c a,b,c 之大,大到 l o n g   l o n g long\ long long long 存不下,后面都用 i n t 128 int128 int128 存储与计算
    加法性质对于 p x p^x px 的影响比较复杂,我们考虑把问题转换成:
    l c m ( x , y ) = c x ≥ a , y ≥ b lcm(x,y)=c\qquad x\ge a,y\ge b lcm(x,y)=cxa,yb
    这样,我们需要最小化 x + y x+y x+y 的问题依然没有变化(两个数都加了常数)
  • 对于 c = ∏ p i q i c=\prod p_i^{q_i} c=piqi ,看 n n n 很小,我们自然可以状压去暴力枚举哪一个数字取到了 q i q_i qi ,即状态为 S S S
    那么,假设某一位为 0 0 0 ,表示这个数字这一位没有取到 p i q i p_i^{q_i} piqi,但是可以取到 p i 0 ∼ p i q i − 1 p_i^0\sim p_i^{q_i-1} pi0piqi1
    考虑到 ∑ q i ≤ 18 \sum q_i\le 18 qi18 ,我们仍然可以去暴力 d f s dfs dfs ,每一位取几次都爆搜
    然后去记录一下,是 S S S 的状态,且大于等于 a a a 的数字 x x x 的最小值 d p a [ S ] dpa[S] dpa[S]
    S S S 的状态,且大于等于 b b b 的数字 y y y 的最小值 d p b [ S ] dpb[S] dpb[S]
  • 然后注意到:如果 T T T S S S超集(也就是 S & T = S S\&T=S S&T=S),且 d p a [ T ] < d p a [ S ] dpa[T]<dpa[S] dpa[T]<dpa[S],那么我们明显取 d p a [ T ] dpa[T] dpa[T] 是更优的,也就是我们需要取 S S S超集的最小值
  • 最后,我们需要获得的答案就是: d p [ S ] + d p [ T ] − a − b dp[S]+dp[T]-a-b dp[S]+dp[T]ab ,其中 S ∣ T = 全 集 S|T=全集 ST=

代码

  • 时间复杂度: O ( n × 2 n ) O(n\times 2^n) O(n×2n)
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
typedef long long ll;
void show(){std::cerr << endl;}template<typename T,typename... Args>void show(T x,Args... args){std::cerr << "[ " << x <<  " ] , ";show(args...);}

const int MAX = 1e6+50;
const int MOD = 998244353;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const double EPS = 1e-5;

ll qpow(ll a,ll n){/* */ll res = 1LL;while(n){if(n&1)res=res*a%MOD;a=a*a%MOD;n>>=1;}return res;}
ll qpow(ll a,ll n,ll p){a%=p;ll res = 1LL;while(n){if(n&1)res=res*a%p;a=a*a%p;n>>=1;}return res;}
ll npow(ll a,ll n){/* */ll res = 1LL;while(n){if(n&1)res=res*a;a=a*a;n>>=1;if(res<0||a<0)return 0;}return res;}
ll inv(ll a){/* */return qpow(a,MOD-2);}
ll inv(ll a,ll p){return qpow(a,p-2,p);}

int n;
lll a,b;
lll p[30],q[30];
lll dpa[1<<19],dpb[1<<19];

void dfs(int x,int S,lll sum){
    if(sum >= a)dpa[S] = min(dpa[S],sum);
    if(sum >= b)dpb[S] = min(dpb[S],sum);
    if(x == n)return;
    dfs(x+1,S,sum);
    for(int i = 1;i < q[x];++i){
        sum *= p[x];
        dfs(x+1,S,sum);
    }
    sum *= p[x];
    dfs(x+1,S|(1<<x),sum);
}

int main()
{
    n = read();
    for(int i = 0;i < n;++i)p[i] = read_lll(),q[i] = read_lll();
    a = read_lll(),b = read_lll();
    memset(dpa,0x3f,sizeof(dpa));
    memset(dpb,0x3f,sizeof(dpb));
    dfs(0,0,1);
    int ed = (1<<n) - 1;
    for(int i = ed;i >= 0;--i){
        for(int j = 0;j < n;++j){
            if(i & (1<<j)){
                dpa[i ^ (1<<j)] = min(dpa[i ^ (1<<j)],dpa[i]);
                dpb[i ^ (1<<j)] = min(dpb[i ^ (1<<j)],dpb[i]);
            }
        }
    }
    lll ans;
    memset(&ans,0x3f,sizeof(ans));
    for(int i = 0;i <= ed;++i)ans = min(ans,dpa[i] + dpb[ed-i] - a - b);
    Print(ans);
    Write();
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值