1 条题解

  • 0
    @ 2025-8-24 22:25:29

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar oisdoaiu
    林地生长于漫宿墙外。每一个研习诸史的人都知道,漫宿无墙

    搬运于2025-08-24 22:25:29,当前版本为作者最后更新于2021-03-16 15:04:11,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    题意

    nn 个物品,每个物品只有 11 个,体积为 bib_i ,求取出物品总体积为 ss 的方案。(模 2642^{64},保证有且仅有一个解)

    bib_i 的生成方式为:随机一个长度为 nnaia_i 序列,满足对于任意 kk 都有:

    (i=1k1ai)<ak性质1(\sum_{i=1}^{k-1}a_i)< a_k\quad \text{性质1} ai<264性质2\sum a_i< 2^{64}\quad\text{性质2}

    再随机一个 rrr<264r<2^{64}rr2642^{64} 互质。然后 biair (mod 264)b_i\equiv a_i\cdot r\ (mod\ 2^{64})

    n64n\leq64

    Case 1

    显然这一个超大背包问题,那么我们学过一个经典的折半的解决方法,复杂度O(2n2)O(2^{\frac n2}),可以解决 n46n\leq46 的部分。

    简单讲一下,就是暴力枚举前一半的所有选择方案,用一个数据结构把所有 (S,sum)(S,sum) 存下来,然后再枚举后一半的所有选择方案,看是否存在对应的前一半的选择方案。

    inline void Solve(){
        int mid = n>>1, tp = 1<<mid;
        for(register int s=0; s<tp; s++){
            ull sum=0;
            for(register int i=1; i<=mid; i++) if((s>>i-1)&1) sum += b[i];
            insert(sum,s);
        }
        tp = 1<<(n-mid);
        for(register int s=0; s<tp; s++){
            ull sum=0;
            for(register int i=1; i<=n-mid; i++) if((s>>i-1)&1) sum += b[mid+i];
            int ps = find(S-sum);
            if(ps!=-1){
                for(register int i=1; i<=mid; i++) if((ps>>i-1)&1) putchar('1'); else putchar('0');
                for(register int i=1; i<=n-mid; i++) if((s>>i-1)&1) putchar('1'); else putchar('0');
                puts("");
                return;
            }
        }
    }
    

    Case 2

    考虑 n(46,64]n\in(46,64] 的部分。

    注意一下 bib_i 的生成方式,如果我们知道了 rr,那么就可以求出 aia_i 和对应的 ss'。根据 aia_i 的性质,可以直接贪心求出答案。

    for(register int i=n; i; i--) 
    	if(S>=a[i]) S -= a[i], ans[i] = '1';
    	else ans[i] = '0';
    

    首先根据性质2,是不用考虑取模的。其次,从大到小考虑时,如果 SakS\geq a_k 而不选 aka_k,根据性质1,就算后面全部选也凑不齐 aka_k,更别说凑齐 SS 了。

    那么核心问题转为了如何求 rr

    根据性质1可以推出 a1<2642na_1<\frac{2^{64}}{2^n},于是得到了 a1a_1 的范围,mx=2642nmx=\frac{2^{64}}{2^n}。又因为 rr 是一个奇数,所以 a1a_1 末尾 00 的个数一定和 b1b_1 末尾 00 的个数一样,设为 cntcnt,那么可能的 aia_i 就只有 2642ncnt1\frac{2^{64}}{2^{n-cnt-1}}个,大概是 2182^{18} 级别。

    于是想到枚举 a1a_1,然后求出 r1r^{-1}。也就是求 a1r1b1 (mod 264)a_1\equiv r^{-1}\cdot b_1\ (mod\ 2^{64}),先同除 2cnt2^{cnt},得到 ar1b (mod 264cnt)a'\equiv r^{-1}\cdot b'\ (mod\ 2^{64-cnt}),此时因为 bb' 是奇数与 264cnt2^{64-cnt} 互质,所以可以直接用快速幂求出 b1b'^{-1}

    ull tp=1ull<<(64-n-cnt-1);
    for(register ull i=0; i<tp; i++){
        ull a1 = (((i<<1)|1)<<cnt);
        ull r = (a1>>cnt)*ksm(tmp,(1ull<<(64-cnt-1))-1); 
        if(cnt) r = r&((1ull<<64-cnt)-1);
    }
    

    但是这样求出的是模 264cnt2^{64-cnt} 意义下的 r1r^{-1},无法求出真正的 r1r^{-1} 的开头 cntcnt 位,所以还要 2cnt2^{cnt} 枚举一下。

    ull tp1=1ull<<cnt;
    for(register ull j=0; j<tp1; j++){
        ull true_r = r|(j<<63-cnt+1);
        check(true_r);
    }
    

    杂项

    代码

    出密码学的都是毒瘤

    快速幂部分可以直接使用自然溢出快速幂,因为 amodb=(amodkb)modba\mod b=(a\mod kb)\mod b

    关于复杂度,设两个 CaseCase 分界点为 kk,那么复杂度分别为 O(2k2)O(2^{\frac k2})O(2nk)O(2^{n-k}),所以理论上 k=2364k=\frac23\cdot64 时取到最优复杂度。

    • 1

    信息

    ID
    6121
    时间
    3000ms
    内存
    512MiB
    难度
    7
    标签
    递交数
    0
    已通过
    0
    上传者