1 条题解

  • 0
    @ 2025-8-24 22:09:46

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar 花淇淋
    物极必反。

    搬运于2025-08-24 22:09:46,当前版本为作者最后更新于2019-05-12 15:35:54,作者可能在搬运后再次修改,您可在原文处查看最新版

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

    以下是正文


    Address

    P5342 [TJOI2019]甲苯先生的线段树

    Solution

    • 对于 c=1c=1,由于路径长度为 O(d)O(d) 级别,只要知道 lca(x,y)lca(x,y) 就是 x,yx,y 二进制下的 lcplcp 就可以做了。

    • 对于 c=2c=2,先和 c=1c=1 一样求出路径编号和,然后要知道一个显然性质和一个神仙性质: (1).(1).xx 二进制中 11 的个数为 cnt(x)cnt(x),则 xx 到根的路径编号和为 2xcnt(x)2x-cnt(x)注意这个性质在根为 00 时也成立,只要 xx 的儿子还是 2x,2x+12x,2x+1 (2).(2). 记路径的编号和为 ss,路径的两个端点为 x,yx,ylca(x,y)=zlca(x,y)=zxzx→z 要经过 aa 条边,yzy→z 要经过 bb 条边。那么当 a,b,sa,b,s 都为定值时,zz 也必为定值。

    • 考虑证明性质 (2):(2): 把路径 xy(x<y)x→y(x<y) 上的每个点编号写成二进制,它们的 lcplcp 就是 zz 的二进制。记 zz 的二进制有 tt 位,那么先算出每个点编号前 tt 位的值的贡献。zz 的贡献是 zzzz 的每个儿子的贡献是 2z2z,每个孙子的贡献是 4z4z,依此类推,可以得出前 tt 位的总贡献 vv 为: z+2z+4z+....+2az+z+2z+4z+...+2bzzz+2z+4z+....+2^{a}z+z+2z+4z+...+2^{b}z-z =(2a+1+2b+13)z=(2^{a+1}+2^{b+1}-3)z
    • kk 为其它贡献,即:vz+k=svz+k=s 讨论 kk 的取值范围(先假设 a,b>0a,b>0): 1.1. zz 往左走 11 步,然后往a1a-1 步到 xxzz 往右走 11 步,然后往b1b-1 步到 yy。路径 xzx→zkk 的贡献为 00yzy→zkk 的贡献为 2b12^b-1,此时 kk 取最值。 2.2. zz 往左走 11 步,然后往a1a-1 步到 xxzz 往右走 11 步,然后往b1b-1 步到 yy。路径 xzx→zkk 的贡献为 2aa12^a-a-1yzy→zkk 的贡献为 2b+1b22^{b+1}-b-2,此时 kk 取最值。 综上所述,k[2b1,2a+2b+1ab3]k∈[2^b-1,2^a+2^{b+1}-a-b-3]
    • 发现 k<vk < v,那么 z=s/v,k=s%vz=s/v,k=s\%vvv 的值只和 a,ba,b 有关,所以 zz 为定值,证毕。

    • 那么问题转化为:找到两个数 p,qp,q(就是 x,yx,y 砍掉前 tt 位之后的值,砍掉前 tt 位后,xx最高位是 00yy 最高位是 11),p<2a1,q<2b,2(p+q)cnt(p)cnt(q)=kp<2^{a-1},q<2^b,2(p+q)-cnt(p)-cnt(q)=k,注意 qq2b12^{b-1} 位必须为 11
    • 枚举 cnt(p)+cnt(q)cnt(p)+cnt(q),得到 a+b=wa+b=w,做数位dpdpf[i][j][k=0/1]f[i][j][k=0/1] 表示从个位开始(a,ba,b 同时填),填了 ii 位,此时共填 jj11,是否进位到第 i+1i+1 位,要求 a+ba+b 的每一位都和 ww 对上即可。
    • 注意细节,注意多开 longlong longlong,注意树高对 a,b,za,b,z 值的限制。
    • 时间复杂度 O(d5)O(d^5),常数显然很小。

    Code

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define ll long long
    
    template <class t>
    inline void read(t & res)
    {
        char ch;
        while (ch = getchar(), !isdigit(ch));
        res = ch ^ 48;
        while (ch = getchar(), isdigit(ch))
        res = res * 10 + (ch ^ 48);
    }
    
    template <class t>
    inline void print(t x)
    {
        if (x > 9) print(x / 10);
        putchar(x % 10 + 48);
    }
    
    const int e = 205;
    ll f[e][e][2], ans;
    bool a1[e], b1[e];
    
    inline ll dp(int a, int b, ll s, int c)
    {
        int i, j, t, x, y, bit = max(max(a - 1, b), (int)log2(s) + 1) + 1; 
        bool flag = a == 3 && b == 2;
        for (i = 0; i < bit; i++)
        for (j = 0; j <= c; j++)
        f[i][j][0] = f[i][j][1] = 0;
        i = 0;
        for (x = 0; x <= 1; x++)
        for (y = 0; y <= 1; y++)
        {
            int k = x + y >> 1, z = x + y & 1;
            if (i == b - 1 && !y) continue;
            if (z != (s & 1)) continue;
            if (i > b - 1 && y) continue;
            if (i > a - 2 && x) continue;
            f[0][x + y][k]++;
        }
        for (i = 0; i < bit - 1; i++)
        {
            ll d = s & (1ll << i + 1);
            if (d) d = 1;
            for (j = 0; j <= c; j++)
            for (t = 0; t <= 1; t++)
            if (f[i][j][t])
            for (x = 0; x <= 1; x++)
            for (y = 0; y <= 1; y++)
            {
                int now = x + y + t, k = now >> 1, z = now & 1;
                if (i + 1 == b - 1 && !y) continue;
                if (z != d) continue;
                if (i + 1 > b - 1 && y) continue;
                if (i + 1 > a - 2 && x) continue;
                f[i + 1][j + x + y][k] += f[i][j][t];
            }
        }
        return f[bit - 1][c][0];
    }
    
    inline ll calc(ll x)
    {
        ll res = x << 1;
        while (x) res -= x & 1, x >>= 1;
        return res;
    }
    
    int main()
    {
        int i, lx, ly, op, tst, h;
        ll x, y, z;
        read(tst);
        while (tst--)
        {
            read(h); read(x); read(y); read(op);
            ll x2 = x, y2 = y;
            lx = ly = z = 0;
            while (x) a1[++lx] = x & 1, x >>= 1;
            while (y) b1[++ly] = y & 1, y >>= 1;
            reverse(a1 + 1, a1 + lx + 1);
            reverse(b1 + 1, b1 + ly + 1);
            for (i = 1; i <= lx && i <= ly; i++)
            if (a1[i] == b1[i]) z = z * 2 + a1[i];
            else break;
            ll s = calc(x2) + calc(y2) - calc(z) - calc(z >> 1);
            if (op == 1)
            {
                print(s);
                putchar('\n');
                continue;
            }
            ans = 0;
            for (int a = 0; a < h; a++)
            for (int b = 0; b < h; b++)
            {
                ll y = ((1ll << a + 1) + (1ll << b + 1) - 3), x = s / y;
                if (!x) continue;
                if ((int)log2(x) + 1 + max(a, b) > h) continue;
                ll k = s - x * y;
                for (int c = 0; c <= a + b; c++) 
                if ((k + c) % 2 == 0) ans += dp(a, b, k + c >> 1, c);
            }
            print(ans - 1);
            putchar('\n');
        }
        return 0;
    }
                ll k = s - x * y;
                for (int c = 0; c <= a + b; c++) 
                if ((k + c) % 2 == 0) ans += dp(a, b, k + c >> 1, c);
            }
            print(ans - 1);
            putchar('\n');
        }
        return 0;
    }
    
    • 1

    信息

    ID
    4336
    时间
    4000ms
    内存
    125MiB
    难度
    7
    标签
    递交数
    0
    已通过
    0
    上传者