• AGC047E Product Simulation


    题目链接

    题意简述

    给定一个长度为 (200,000) 的数组 (a),编号从 (0) 开始,初始时 (a_0=A,a_1=B),但你不知道 (A)(B)

    你可以执行不超过 (200,000) 次操作,每次操作如下两种:

    (1)+ i j k:让 (a_kleftarrow a_i+a_j)

    (2)< i j k:让 (a_kleftarrow a_i<a_j)

    保证 (0le A,Ble 10^9),在任何时候 (a) 数组的元素都必须在 ([0,10^{19}])

    输出一个操作序列(注意此题无输入),使得所有操作结束之后,(a_2=A imes B)

    Solution

    首先我们需要搞出 (1),这个利用 (a_{199999}<a_0) 即可直接得到。(a_0=0) 另当别论。

    基本操作之一:两个 (0/1) 值的非和与运算

    非:( ext{not }x=x<1)

    与:(x ext{ and }y=1<x+y)

    基本操作之二:根据 (0/1) 状态进行赋值

    这好像没有什么好做法,但我们可以实现让 (x) 变成 (egin{cases}2^z & y=1\0 & y=0end{cases})(注意这个 (z) 是已知的,不是数组内的元素)。

    可以视为 (xleftarrow 2^z imes y),故把 (x) 赋成 (y) 之后自加 (z) 次即可。

    基本操作之三:二进制分解

    即把 (x) 二进制分解的结果保存在 (b_{0dots 30}) 内。

    记录一个临时变量初始 (y=0),然后从高到低位考虑,把 (b_i) 设定为 ( ext{not }(x<y+2^i)),然后根据 (b_i) 的值为 (0)(1) 来决定是否要将 (y) 加上 (2^i),可以使用基本操作二。

    总做法

    有了三个基本操作之后,思路就比较自然了。

    (a_0)(a_1) 分别二进制分解后做个卷积((0/1) 值的相乘可用 ( ext{and}))得到答案的 (60) 位二进制表示之后依次加到 (a_2) 内即可。

    不难发现,当 (a_0=0) 时上面的算法可以得出正确结果。操作次数为 (O(log^2(10^9)))

    Code

        #include <bits/stdc++.h>
         
        const int ZERO = 199998, ONE = 199999, N = 2e5 + 5;
         
        int tot, a[N], b[N], c[N];
        char ty[N];
         
        void push(char t, int x, int y, int z)
        {
        	ty[++tot] = t; a[tot] = x; b[tot] = y; c[tot] = z;
        }
         
        void make(int x, int y, int c)
        {
        	push('+', y, ZERO, x);
        	for (int i = 1; i <= c; i++) push('+', x, x, x);
        }
         
        void NOT(int x) {push('<', x, ONE, x);}
         
        void AND(int x, int y, int z) {push('+', x, y, z); push('<', ONE, z, z);}
         
        void bin(int x, int offset)
        {
        	push('+', ZERO, ZERO, x + 3);
        	for (int i = 30; i >= 0; i--)
        	{
        		push('+', x + 3, 100 + i, x + 5);
        		push('<', x, x + 5, offset + i);
        		NOT(offset + i); make(x + 5, offset + i, i);
        		push('+', x + 3, x + 5, x + 3);
        	}
        }
         
        int main()
        {
        	push('<', ZERO, 0, ONE);
        	push('+', ZERO, ONE, 100);
        	for (int i = 1; i <= 30; i++) push('+', 99 + i, 99 + i, 100 + i);
        	bin(0, 1000); bin(1, 2000);
        	for (int i = 0; i <= 30; i++) for (int j = 0; j <= 30; j++)
        		AND(1000 + i, 2000 + j, 2500), push('+', 2500,
        			3000 + i + j, 3000 + i + j);
        	for (int i = 0; i <= 60; i++) make(4000, 3000 + i, i),
        		push('+', 2, 4000, 2);
        	std::cout << tot << std::endl;
        	for (int i = 1; i <= tot; i++) printf("%c %d %d %d
    ", ty[i],
        		a[i], b[i], c[i]);
        	return 0;
        }
    
  • 相关阅读:
    轻松学习Linux之AWK使用初步
    轻松学习Linux之理解Shell的硬链接与软连接
    轻松学习Linux之自动执行任务
    轻松学习Linux系统安装篇之fdisk命令行工具的使用
    Leetcode-1030 Next Greater Node In Linked List(链表中的下一个更大节点)
    Leetcode-1028 Convert to Base -2(负二进制转换)
    Leetcode-1029 Binary Prefix Divisible By 5(可被 5 整除的二进制前缀)
    ACM模板——2的次方表
    ACM模板——快速幂
    ACM模板——素数相关
  • 原文地址:https://www.cnblogs.com/xyz32768/p/13829261.html
Copyright © 2020-2023  润新知