• Solution -「OurOJ 46544」漏斗计算


    (mathcal{Description})

      Link.

      定义一个运算结点 (u) 有两个属性:当前容量 (x_u)、最大容量 (V_u)。提供以下单元操作:

    1. I 读入一个整数 (x),令新结点 (u=(x,x))

    2. F u 装满 (u) 结点,即令 (x_u=V_u)

    3. E u 清空 (u) 结点,即令 (x_u=0)

    4. C s 令新结点 (u=(0,s))

    5. M u 令新结点 (v=(0,x_u))

    6. T u v 不断令 (x_uleftarrow x_u-1,x_vleftarrow x_v-1) 直到 (x_u=0)(x_v=V_v)

      构造不超过 (10^4) 次操作的一个运算方法,输入 (a,b),输出 (abmod 2^{18})

      (0le a,ble 10^5)

    (mathcal{Solution})

      貌似是瓶子国的某个点?反正很离谱就是了。

      首先观察操作,我们只有一个二元运算 (T(u,v)),必然只能用它实现逻辑模块。

      再从问题入手,不难想到可以龟速乘计算 (ab),过程中若值 (ge 2^{18}),则减去 (2^{18})。那么我们至少需要实现这些模块:

    • 加法器:输入结点 (u,v),输出 (w),满足 (x_w=x_u+x_v)

    • 逻辑减法器:输入结点 (u,v),输出 (w),若 (x_uge x_v)(x_w=x_u-x_v);否则 (x_w=x_u)

      加法器比较方便:新建一个大容量点,把两个加数复制一份倒进去就好。先实现一个复制当前容量器:

    inline int copyNum( const int u ) {
        printf( "M %d
    ", u ), ++node;
        printf( "F %d
    ", node );
        return node;
    }
    

      再实现加法器:

    inline int add( const int u, const int v ) {
        printf( "C 1000000000
    " ); int res = ++node;
        printf( "T %d %d
    ", copyNum( u ), res );
        printf( "T %d %d
    ", copyNum( v ), res );
        return res;
    }
    

      逻辑减法?先要判断大小关系。而 (T(u,v)) 之后 (x'_u=max{x_u-x_v,0}),我们只需要判断一个结点的当前容量是否为 (0)。好消息是,我们能够实现逻辑非器:

    inline int logicNot( const int u ) {
        printf( "M %d
    ", u ), ++node;
        puts( "C 1" ), ++node;
        printf( "F %d
    ", node );
        printf( "T %d %d
    ", node, node - 1 );
        return node;
    }
    

      内部逻辑比较易懂就不讲啦。在此基础上,实现普通减法器和逻辑减法器:

    inline int sub( const int u, const int v ) {
        printf( "M %d
    ", v ); int tmp = ++node;
        printf( "T %d %d
    ", copyNum( u ), tmp );
        return node;
    }
    
    inline PII logicSub( int u, int v ) {
        int f = logicNot( sub( u = add( u, 1 ), v ) );
        rep ( i, 1, 18 ) f = add( f, f );
        printf( "M %d
    ", f ), f = ++node;
        printf( "T %d %d
    ", v = copyNum( v ), f );
        return { u = sub( sub( u, v ), 1 ), f };
    }
    

      逻辑减法器返回的 first 即减法结果,second 用于求解时重复利用。

      底层方法实现之后,剩下的工作就简单了:输入 (a,b),计算 (2a,4a,cdots,2^{16}a),枚举 (b) 是否大于等于 (2^{16}dots2^0),若是,则减去,答案加上对应的权。都能用以逻辑非为基础的模块实现。

      操作次数复杂度为 (mathcal O(log^2 V))(倍增以及内部的逻辑减法),我实现的常数较大,不过封装成模块很易懂就是了。(

    (mathcal{Code})

    /*+Rainybunny+*/
    
    #include <bits/stdc++.h>
    
    #define rep( i, l, r ) for ( int i = l, rep##i = r; i <= rep##i; ++i )
    #define per( i, r, l ) for ( int i = r, per##i = l; i >= per##i; --i )
    
    typedef std::pair<int, int> PII;
    #define fi first
    #define se second
    
    int node, mod;
    
    namespace StandardModuleLibrary {
    
    inline int logicNot( const int u ) {
        printf( "M %d
    ", u ), ++node;
        puts( "C 1" ), ++node;
        printf( "F %d
    ", node );
        printf( "T %d %d
    ", node, node - 1 );
        return node;
    }
    
    inline int copyNum( const int u ) {
        printf( "M %d
    ", u ), ++node;
        printf( "F %d
    ", node );
        return node;
    }
    
    inline int add( const int u, const int v ) {
        printf( "C 1000000000
    " ); int res = ++node;
        printf( "T %d %d
    ", copyNum( u ), res );
        printf( "T %d %d
    ", copyNum( v ), res );
        return res;
    }
    
    inline int sub( const int u, const int v ) {
        printf( "M %d
    ", v ); int tmp = ++node;
        printf( "T %d %d
    ", copyNum( u ), tmp );
        return node;
    }
    
    inline PII logicSub( int u, int v ) {
        int f = logicNot( sub( u = add( u, 1 ), v ) );
        rep ( i, 1, 18 ) f = add( f, f );
        printf( "M %d
    ", f ), f = ++node;
        printf( "T %d %d
    ", v = copyNum( v ), f );
        return { u = sub( sub( u, v ), 1 ), f };
    }
    
    } using namespace StandardModuleLibrary;
    
    int main() {
        freopen( "liver.out", "w", stdout );
    
        int x, y;
        printf( "C 1
    F 1
    " ), node = 1; // element 1.
        printf( "C 262144
    F 2
    " ), mod = node = 2; // module.
        puts( "I" ), x = ++node;
        puts( "I" ), y = ++node;
    
        int buc[30] = { x }, pwr[30] = {};
        int ans = ++node; puts( "C 1000000000" );
    
        pwr[0] = ++node, printf( "C 1
    F %d
    ", node );
        rep ( i, 1, 16 ) {
            pwr[i] = ++node, printf( "C %d
    F %d
    ", 1 << i, node );
            PII shit( logicSub( add( buc[i - 1], buc[i - 1] ), mod ) );
            buc[i] = shit.fi;
        }
    
        per ( i, 16, 0 ) {
            PII r( logicSub( y, pwr[i] ) );
            int x = r.se;
            rep ( j, 1, 18 - i ) x = add( x, x );
            printf( "M %d
    ", x ), x = ++node;
            printf( "T %d %d
    ", buc[i], x ), y = r.fi;
            ans = logicSub( add( ans, buc[i] ), mod ).fi;
        }
    
        printf( "P %d
    ", ans );
        return 0;
    }
    
    
  • 相关阅读:
    CentOS安装node.js-8.11.1+替换淘宝NPM镜像
    【推荐】CentOS安装gcc-4.9.4+更新环境+更新动态库
    申请安装阿里云免费SSL证书
    服务器安全加固
    【推荐】优秀代码
    CenOS登录失败处理功能
    Ubuntu修改密码及密码复杂度策略设置
    mysql 5.7添加server_audit 安全审计功能
    快速安装jumpserver开源堡垒机
    oracle 11g 配置口令复杂度
  • 原文地址:https://www.cnblogs.com/rainybunny/p/15428124.html
Copyright © 2020-2023  润新知