AGC047E Product Simulation

题目链接

题意简述

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

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

(1)+ i j k:让 \(a_k\leftarrow a_i+a_j\)

(2)< i j k:让 \(a_k\leftarrow a_i<a_j\)

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

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

Solution

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

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

非:\(\text{not }x=x<1\)

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

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

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

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

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

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

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

总做法

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

\(a_0\)\(a_1\) 分别二进制分解后做个卷积(\(0/1\) 值的相乘可用 \(\text{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\n", ty[i],
    		a[i], b[i], c[i]);
    	return 0;
    }
posted @ 2020-10-18 11:05  epic01  阅读(253)  评论(0编辑  收藏  举报