【无码专区11】异或2(结论 / 推式子 + 哈希hash + 大整数高精度 加减乘除重载考察)

本题已自我实现。但仍归于无码专区

problem

∑ i = 1 n − 1 i ⨁ ( n − i ) \sum_{i=1}^{n-1}i\bigoplus (n-i) i=1n1i(ni)

20 % , n ≤ 1 e 6 ; ; 50 % , n ≤ 1 e 9 ; ; 70 % , n ≤ 1 e 18 ; ; 100 % , n ≤ 1 0 500 20\%,n\le 1e6;;50\%,n\le 1e9;;70\%,n\le 1e18;;100\%,n\le 10^{500} 20%,n1e6;;50%,n1e9;;70%,n1e18;;100%,n10500

1 s , 128 M B 1s,128MB 1s,128MB

my idea

当看到 n n n 的数据范围且不取模的那一刻,我就知道大整数跑不掉了。

最基础 20 % 20\% 20% ,直接暴力做。

会发现 i ⨁ ( n − i ) = ( n − i ) ⨁ i i\bigoplus (n-i)=(n-i)\bigoplus i i(ni)=(ni)i,所以其实上是 2 ∑ i = 1 n − 1 2 i ⨁ ( n − i ) 2\sum_{i=1}^{\frac{n-1}{2}}i\bigoplus (n-i) 2i=12n1i(ni) 大概这样的。

我曾试图每一位单独考虑,但很难下手。

直观地,我能感受到是 l o g log log 的正解。

但由于对异或计算法则的不了解,我觉得自己并不能做出这道题来。

solution

i ⨁ ( n − i ) = ( n − i ) ⨁ i ⇒ 2 ∑ i = 1 n − 1 2 i ⨁ ( n − i ) i\bigoplus (n-i)=(n-i)\bigoplus i\Rightarrow 2\sum_{i=1}^{\frac{n-1}{2}}i\bigoplus (n-i) i(ni)=(ni)i2i=12n1i(ni),真的就是正解的关键所在。

如果我在考场上继续细想,应该是能找到规律的。

  • n = 2 k + 1 n=2k+1 n=2k+1

    f ( n ) = ∑ i = 1 n − 1 i ⨁ ( n − i ) = 2 ∑ i = 1 k 2 i ⨁ ( 2 k + 1 − 2 i ) f(n)=\sum_{i=1}^{n-1}i\bigoplus (n-i)=2\sum_{i=1}^k2i\bigoplus(2k+1-2i) f(n)=i=1n1i(ni)=2i=1k2i(2k+12i)

    因为 2 i , 2 k , 2 k − 2 i 2i,2k,2k-2i 2i,2k,2k2i 均为偶数,所以每次异或, 2 0 2^0 20 为是 1 1 1

    ⇒ f ( n ) = 2 ∑ i = 1 k 2 i ⨁ ( 2 k − 2 i ) + 2 k \Rightarrow f(n)=2\sum_{i=1}^k2i\bigoplus (2k-2i)+2k f(n)=2i=1k2i(2k2i)+2k

    参与异或的只有偶数,整体右移 1 1 1

    ⇒ f ( n ) = 4 ∑ i = 1 k i ⨁ ( n − i ) + 2 k \Rightarrow f(n)=4\sum_{i=1}^ki\bigoplus (n-i)+2k f(n)=4i=1ki(ni)+2k

    感觉很有机会转化成一半的子问题,把 i = k i=k i=k 提出来。

    ⇒ f ( n ) = 4 ∑ i = 1 k − 1 i ⨁ ( n − i ) + 4 ( k ⨁ 0 ) + 2 k \Rightarrow f(n)=4\sum_{i=1}^{k-1}i\bigoplus (n-i)+4(k\bigoplus0)+2k f(n)=4i=1k1i(ni)+4(k0)+2k

    ⇒ f ( n ) = 4 f ( k ) + 6 k \Rightarrow f(n)=4f(k)+6k f(n)=4f(k)+6k

  • n = 2 k n=2k n=2k

    f ( n ) = ∑ i = 1 n − 1 i ⨁ ( n − i ) = ∑ i = 1 k 2 i ⨁ ( 2 k − 2 i ) + ∑ i = 1 k ( 2 i − 1 ) ⨁ ( 2 k − 2 i + 1 ) f(n)=\sum_{i=1}^{n-1}i\bigoplus (n-i)=\sum_{i=1}^k2i\bigoplus(2k-2i)+\sum_{i=1}^k(2i-1)\bigoplus(2k-2i+1) f(n)=i=1n1i(ni)=i=1k2i(2k2i)+i=1k(2i1)(2k2i+1)

    ⇒ f ( n ) = 2 f ( k ) + ∑ i = 0 k − 1 ( 2 i + 1 ) ⨁ ( 2 k − 2 i − 1 ) \Rightarrow f(n)=2f(k)+\sum_{i=0}^{k-1}(2i+1)\bigoplus(2k-2i-1) f(n)=2f(k)+i=0k1(2i+1)(2k2i1)

    ⇒ f ( n ) = 2 f ( k ) + ∑ i = 0 k − 1 ( 2 i + 1 ) ⨁ [ 2 ( k − 1 ) − 2 i + 1 ] \Rightarrow f(n)=2f(k)+\sum_{i=0}^{k-1}(2i+1)\bigoplus[2(k-1)-2i+1] f(n)=2f(k)+i=0k1(2i+1)[2(k1)2i+1]

    2 i + 1 2i+1 2i+1 是奇数, 2 ( k − 1 ) − 2 i + 1 2(k-1)-2i+1 2(k1)2i+1 也是奇数,所以异或后 2 0 2^0 20 二进制位一定是 0 0 0

    那么就不管了,直接整体右移 1 1 1

    ⇒ f ( n ) = 2 f ( k ) + 2 ∑ i = 0 k − 1 i ⨁ ( k − 1 − i ) \Rightarrow f(n)=2f(k)+2\sum_{i=0}^{k-1}i\bigoplus(k-1-i) f(n)=2f(k)+2i=0k1i(k1i)

    感觉很有机会转化成一半的子问题,把 i = 0 , i = k − 1 i=0,i=k-1 i=0,i=k1 提出来。

    ⇒ f ( n ) = 2 f ( k ) + 4 ( k − 1 ) + 2 ∑ i = 1 k − 2 i ⨁ ( k − 1 − i ) \Rightarrow f(n)=2f(k)+4(k-1)+2\sum_{i=1}^{k-2}i\bigoplus(k-1-i) f(n)=2f(k)+4(k1)+2i=1k2i(k1i)

    ⇒ f ( n ) = 2 f ( k ) + 2 f ( k − 1 ) + 4 k − 4 \Rightarrow f(n)=2f(k)+2f(k-1)+4k-4 f(n)=2f(k)+2f(k1)+4k4

直接记忆化搜索,最后就是要写大整数加减乘除赋值了。

trick
因为我曾试图用 map<Int,Int> 来记录 f [ n ] f[n] f[n],但是没写过。
重载 SLT 内部运算真的搞崩了。
所以我将字符串进行了自然溢出的 hash
map<ull,Int> 就不用管重载 map< 了。
最后就是空间比较小,我写的是数组,不是 vector 存大数。
只知道 1000 太小,1500 太大。

code

#include <bits/stdc++.h>
using namespace std;
#define ull unsigned long long
struct Int {
    int len, v[1300]; ull Hash;
    Int() { len = 1; memset( v, 0, sizeof( v ) ); }
    Int( int x ) { memset( v, 0, sizeof( v ) ); v[len = 1] = x; calc(); }
    void read() {
        char s = getchar(); len = 0;
        while( '0' <= s and s <= '9' ) {
            v[++ len] = s ^ 48;
            s = getchar();
        }
        reverse( v + 1, v + len + 1 );
    }
    void print() { for( int i = len;i;i -- ) printf( "%d", v[i] ); puts(""); }
	void calc() { Hash = 1; for( int i = 1;i <= len;i ++ ) Hash = Hash * 131 + v[i]; }
	
    Int operator + ( Int x ) {
        Int ans;
        int ip = 1;
        while( ip <= len or ip <= x.len ) {
            ans.v[ip + 1] = ( v[ip] + x.v[ip] + ans.v[ip] ) / 10;
            ans.v[ip] = ( v[ip] + x.v[ip] + ans.v[ip] ) % 10;
            ip ++;
        }
        while( ip > 1 and ! ans.v[ip] ) ip --;
        ans.len = ip;
        ans.calc();
        return ans;
    }

    Int operator - ( int k ) {
        Int ans;
        Int x( k );
        int ip = 1;
        while( ip <= len or ip <= x.len ) {
            ans.v[ip] = v[ip] - x.v[ip];
            if( ans.v[ip] < 0 ) v[ip + 1] --, ans.v[ip] += 10;
			ip ++;
        }
        while( ip > 1 and ! ans.v[ip] ) ip --;
        ans.len = ip;
        ans.calc();
        return ans;
    }

    Int operator * ( int x ) {
        Int ans;
        ans.len = len + 1;
        for( int i = 1;i <= len;i ++ ) {
            ans.v[i + 1] = ( ans.v[i] + v[i] * x ) / 10;
            ans.v[i] = ( v[i] * x + ans.v[i] ) % 10;
        }
        while( ans.len > 1 and ! ans.v[ans.len] ) ans.len --;
        ans.calc();
        return ans;
    }

    Int operator / ( int x ) {
        Int ans; int k = 0;
        ans.len = len;
        for( int i = len;i;i -- ) {
            k = ( k << 1 ) + ( k << 3 ) + v[i];
            ans.v[i] = k / x;
            k %= x;
        }
        while( ans.len > 1 and ! ans.v[ans.len] ) ans.len --;
        ans.calc();
        return ans;
    }

    Int operator = ( Int x ) {
        len = x.len;
        Hash = x.Hash;
        memcpy( v, x.v, sizeof( x.v ) );
        return *this;
    }

};
map < ull, Int > f;

Int dfs( Int n ) {
    if( f.count( n.Hash ) ) return f[n.Hash];
    if( n.len == 1 and ( n.v[1] == 1 or n.v[1] == 0 ) ) return (Int)0;
    if( n.v[1] & 1 ) f[n.Hash] = dfs( n / 2 ) * 4 + n / 2 * 6;
    else f[n.Hash] = dfs( n / 2 ) * 2 + dfs( n / 2 - 1 ) * 2 + n * 2 - 4;
	return f[n.Hash];
}

signed main() {
    freopen( "rox.in", "r", stdin );
    freopen( "rox.out", "w", stdout );
    Int n;
    n.read(); 
    dfs( n ).print();
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值