P1737 [NOI2016] 旷野大计算AC题解

本人 6年级 耗时2个月luoguP1737 [NOI2016] 旷野大计算AC

接下来由我为大家讲解

输出的是 𝑎a 的二进制表示,肯定只包含 00 和 11 。

可以想到,这题又要用 𝑆(𝑥)S(x) 函数来搞出 00 和 11 。

用 𝑆(𝑥)S(x) 获得 00 和 11 要求自变量 𝑥x 有正负两种取值,所以我们构造这个正负的取值。

考虑 𝑎a 的 22 进制的最高位,即第 3131 位,很显然,若这一位为 11 ,则 𝑎⩾231a⩾231 ;若这一位为 00 ,则 𝑎<231a<231 ,于是我们减去 231231 就能搞出正负不同的取值了。

同样的,我们需要加上 10−1010−10 来保证 𝑆(𝑥)S(x) 不会恰好取到 1221​ 。

由此我们发现,𝑆(𝑥)S(x) 函数能够用来比较变量和某个常数之间的大小关系。

我们至少需要将减去 231231 后的数左移 4141 位以爆掉精度。这是 task4task4 中计算过的。

所以我们计算最高位时就输出 𝑆((𝑎−231)×241)S((a−231)×241) ,即 𝑆(𝑎×241−272)S(a×241−272) 。

我们得到 00 或 11 后,把它左移恰当位数后再从原数中减去,得到 𝑎′a′ ,就可以继续对下一位搞出正负两种取值了。第二高位答案就是 𝑆((𝑎′−230)×241)=𝑆(𝑎′×241−271)S((a′−230)×241)=S(a′×241−271) 。

诶?还是乘 4141 哎!

于是我们发觉,可以一开始就将 𝑎a 左移 4141 位,以减少计算节点数。

但这样搞就需要我们计算出 242242 到 272272 这 3131 个幂次方了。计算这些幂次方可以用高精(三个 𝑙𝑜𝑛𝑔 𝑙𝑜𝑛𝑔long long 拼起来就足够了),可以用 pow 函数,也可以用计算器来手动模拟压位高精。

当然最靠谱的方法就是用题目提供的 checker 来计算。写好计算节点,输入 11 ,让 checker 自己跑出来 242242 到 272272 。省时省力。

接下来送出AC代码 代码为C++98 运行

感谢大家的收看

#include <bits/stdc++.h>

using namespace std;
int tot;

int In(){
    printf("I\n");
    return ++tot;
}
int Out(int x){
    printf("O %d\n", x);
    return ++tot;
}
int add(int x, int y){
    printf("+ %d %d\n", x, y);
    return ++tot;
}
int addc(int x, int c){
    printf("C %d %d\n", x, c);
    return ++tot;
}
int addc(int x, double c){
    printf("C %d %.10lf\n", x, c);
    return ++tot;
}
int addc(int x, char* c){
    printf("C %d %s\n", x, c);
    return ++tot;
}
int addc(int x, string c){
    printf("C %d %s\n", x, c.c_str());
    return ++tot;
}
int neg(int x){
    printf("- %d\n", x);
    return ++tot;
}
int lsh(int x, int c){
    printf("< %d %d\n", x, c);
    return ++tot;
}
int rsh(int x, int c){
    printf("> %d %d\n", x, c);
    return ++tot;
}
int sig(int x){
    printf("S %d\n", x);
    return ++tot;
}

int sgn(int x){
    int a = lsh(x, 500);
    a = sig(a);
    a = lsh(a, 1);
    a = addc(a, -1);
    return a;
}
int p(int x){
    return sig(lsh(x, 500));
}
int Abs(int x){
    int b = lsh(p(addc(x, "0.00000000000000000000001")), 152);
    int c = rsh(x, 150);
    int d = sig(add(c, b));
    d = addc(d, -0.5); d = neg(d);
    d = lsh(d, 153); d = add(d, b);
    return add(x, d);
}
int min0(int x){
    int b = lsh(p(addc(x, "0.00000000000000000000001")), 151);
    int c = rsh(x, 150);
    int d = sig(add(c, b));
    d = addc(d, -0.5); b = neg(b);
    d = lsh(d, 152); d = add(d, b);
    return d;
}
int max0(int x, int y, int c){
    x = neg(x);
    int b = lsh(p(x), 151);
    int d = sig(add(c, b));
    d = addc(d, -0.5); b = neg(b);
    d = lsh(d, 152); d = add(d, b);
    return d;
}
int a[50],b[50];
int turn10(int *a){
    int ret = add(a[0], lsh(a[1], 1));
    for(int i = 2; i <= 31; i ++){
        ret = add(ret, lsh(a[i], i));
    }
    return ret;
}
void turn2(int x, int *a){
    x = lsh(x, 500);
    for(int i = 31; i >= 1; i --){
        int tmp = addc(x, (-pow(2, i) + 0.01) * pow(2, 500));
        tmp = sig(tmp);
        a[i] = tmp;
        tmp = neg(lsh(tmp, 500 + i));
        x = add(x, tmp);
    }
    a[0] = rsh(x, 500);
}
int xor1(int x, int y){
    int a = add(x, y);
    int b = addc(a, -1.5);
    b = neg(p(b));
    b = lsh(b, 1);
    return add(a, b);
}
void xor32(int *a, int *b){
    for(int i = 0; i <= 31; i ++) a[i] = xor1(a[i], b[i]);
}
int div10(int x){
    string a = "2.06343706889556054705";
    string sa = "-0.887298334620741688550198422716226773933599412474263948504833186119911079463425709202638465";    
    return lsh(addc(sig(addc(rsh(x, 150), a)), sa), 150);
}
int modadd(int x, int y, int cc, int rshc, int negc){
    int s = add(x, y);
    int tmp = add(s, cc);
    return add(s, max0(tmp, negc, rshc));
}
int main(){
	int n;
	cin>>n;
    if(n==1 || n==7){
    	int a = In(), b = In();
	    a = add(a, b);
	    a = lsh(a, 1);
	    a = neg(a);
	    Out(a);
    	
	}
   	if(n==2){
   		int a = In();
	    int b = a;
	    a = lsh(a, 4);
	    a = add(a, b);
	    a = neg(a);
	    Out(sig(a));
	} 
	if(n==3){
		int a = In();
	    a = lsh(a, 500);
	    a = sig(a);
	    a = lsh(a, 1);
	    a = addc(a, -1);
	    Out(a);
	}
	if(n==4){
		int a = In();
    	Out(Abs(a));
	}
	if(n==5){
		for(int i = 31; i >= 0; i --){
	        a[i] = In();
	    }
	    Out(turn10(a));

	}
	if(n==6){
		int x = In();
	    turn2(x, a);
	    for(int i = 31; i >= 0; i --) Out(a[i]);
	}
	if(n==8){
		int x = In();
    	Out(div10(x));
	}
	if(n==9){
		for(int i = 1; i <= 16; i ++){
	        a[i] = In();
	    }
	    for(int i = 1; i < 16; i ++){
	        for(int j = i + 1; j <= 16; j ++){
	            int t = add(a[i], a[j]);
	            a[i] = add(a[i], min0(add(neg(a[i]), a[j])));
	            a[j] = add(t, neg(a[i]));
	        }
	    }
	    for(int i = 1; i <= 16; i ++){
	        Out(a[i]);
	    }

	}
	if(n==10){
		int x = In(); int y = In(); int c = In(); int negc = neg(c); int rshc = rsh(negc, 150); int cc = addc(negc, "0.0000001");
	    turn2(x, a);
	    x = rsh(x, 1000);
	    for(int i = 31; i >= 0; i --){
	        x = add(x, x);
	        x = modadd(x, a[i], cc, rshc, negc);
	    }
	    turn2(y, b);
	    int d = x; x = rsh(x, 1000);
	    for(int i = 31; i >= 0; i --){
	        x = modadd(x, x, cc, rshc, negc);
	        x = modadd(x, max0(addc(b[i], "-0.5"), d, rsh(d, 150)), cc, rshc,negc);
	    }
	    Out(x);
	}
    return 0;
}
  • 10
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值