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

Address

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

Solution

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

  • 对于 c = 2 c=2 c=2,先和 c = 1 c=1 c=1 一样求出路径编号和,然后要知道一个显然性质和一个神仙性质:
    ( 1 ) . (1). (1). x x x 二进制中 1 1 1 的个数为 c n t ( x ) cnt(x) cnt(x),则 x x x 到根的路径编号和为 2 x − c n t ( x ) 2x-cnt(x) 2xcnt(x)注意这个性质在根为 0 0 0 时也成立,只要 x x x 的儿子还是 2 x , 2 x + 1 2x,2x+1 2x,2x+1
    ( 2 ) . (2). (2). 记路径的编号和为 s s s,路径的两个端点为 x , y x,y x,y l c a ( x , y ) = z lca(x,y)=z lca(x,y)=z x → z x→z xz 要经过 a a a 条边, y → z y→z yz 要经过 b b b 条边。那么当 a , b , s a,b,s a,b,s 都为定值时, z z z 也必为定值。

  • 考虑证明性质 ( 2 ) : (2): (2):
    把路径 x → y ( x &lt; y ) x→y(x&lt;y) xy(x<y) 上的每个点编号写成二进制,它们的 l c p lcp lcp 就是 z z z 的二进制。记 z z z 的二进制有 t t t 位,那么先算出每个点编号前 t t t 位的值的贡献。 z z z 的贡献是 z z z z z z 的每个儿子的贡献是 2 z 2z 2z,每个孙子的贡献是 4 z 4z 4z,依此类推,可以得出前 t t t 位的总贡献 v v v 为:
    z + 2 z + 4 z + . . . . + 2 a z + z + 2 z + 4 z + . . . + 2 b z − z z+2z+4z+....+2^{a}z+z+2z+4z+...+2^{b}z-z z+2z+4z+....+2az+z+2z+4z+...+2bzz
    = ( 2 a + 1 + 2 b + 1 − 3 ) z =(2^{a+1}+2^{b+1}-3)z =(2a+1+2b+13)z
  • k k k 为其它贡献,即: v z + k = s vz+k=s vz+k=s
    讨论 k k k 的取值范围(先假设 a , b &gt; 0 a,b&gt;0 a,b>0):
    1. 1. 1. z z z 往左走 1 1 1 步,然后往 a − 1 a-1 a1 步到 x x x z z z 往右走 1 1 1 步,然后往 b − 1 b-1 b1 步到 y y y。路径 x → z x→z xz k k k 的贡献为 0 0 0 y → z y→z yz k k k 的贡献为 2 b − 1 2^b-1 2b1,此时 k k k 取最值。
    2. 2. 2. z z z 往左走 1 1 1 步,然后往 a − 1 a-1 a1 步到 x x x z z z 往右走 1 1 1 步,然后往 b − 1 b-1 b1 步到 y y y。路径 x → z x→z xz k k k 的贡献为 2 a − a − 1 2^a-a-1 2aa1 y → z y→z yz k k k 的贡献为 2 b + 1 − b − 2 2^{b+1}-b-2 2b+1b2,此时 k k k 取最值。
    综上所述, k ∈ [ 2 b − 1 , 2 a + 2 b + 1 − a − b − 3 ] k∈[2^b-1,2^a+2^{b+1}-a-b-3] k[2b1,2a+2b+1ab3]
  • 发现 k &lt; v k &lt; v k<v,那么 z = s / v , k = s % v z=s/v,k=s\%v z=s/v,k=s%v v v v 的值只和 a , b a,b a,b 有关,所以 z z z 为定值,证毕。

  • 那么问题转化为:找到两个数 p , q p,q p,q(就是 x , y x,y x,y 砍掉前 t t t 位之后的值,砍掉前 t t t 位后, x x x最高位是 0 0 0 y y y 最高位是 1 1 1), p &lt; 2 a − 1 , q &lt; 2 b , 2 ( p + q ) − c n t ( p ) − c n t ( q ) = k p&lt;2^{a-1},q&lt;2^b,2(p+q)-cnt(p)-cnt(q)=k p<2a1,q<2b,2(p+q)cnt(p)cnt(q)=k,注意 q q q 2 b − 1 2^{b-1} 2b1 位必须为 1 1 1
  • 枚举 c n t ( p ) + c n t ( q ) cnt(p)+cnt(q) cnt(p)+cnt(q),得到 a + b = w a+b=w a+b=w,做数位 d p dp dp f [ i ] [ j ] [ k = 0 / 1 ] f[i][j][k=0/1] f[i][j][k=0/1] 表示从个位开始( a , b a,b a,b 同时填),填了 i i i 位,此时共填 j j j 1 1 1,是否进位到第 i + 1 i+1 i+1 位,要求 a + b a+b a+b 的每一位都和 w w w 对上即可。
  • 注意细节,注意多开 l o n g long long l o n g long long,注意树高对 a , b , z a,b,z a,b,z 值的限制。
  • 时间复杂度 O ( d 5 ) O(d^5) O(d5),常数显然很小。

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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值