UOJ224 NOI2016 旷野大计算 构造、造计算机

传送门——UOJ

传送门——Luogu


 这段时间请不要找Itst聊天,Itst已经做疯了

事实证明大模拟题不可做

query 1

送分,加起来一起乘即可

I
I
+ 1 2
< 3 1
- 4
O 5

query 2

送分

I
- 1
< 2 4
+ 2 3
S 4
O 5

query 3

送分,把原数扩大$INF$倍,再调用$s$函数即可

默认下面的$INF=2^{100}$

I
< 1 100
S 2
C 3 -0.5
< 4 1
O 5

query 4

好火啊qaq

因为我们的操作只允许我们放大到无穷大和缩小到趋近于$0$,所以我们考虑当$x$趋近于$0$时式子会趋向于什么

因为$s'(x) = ((1+e^{-x})^{-1}) ' = -(1+e^{-x})^{-2} \times (1+e^{-x})'$

$(1+e^{-x}) ' = (e^{-x}) ' = -e^{-x}$

所以$s'(x) = \frac{e^{-x}}{(1 + e^{-x})^2}$

将$0$带入得$s'(0)=\frac{1}{4}$

也就是说当$x$趋近于$0$时,函数可以近似为一个一次函数:$y=\frac{1}{4}x + 0.5$

而$s(x \times INF)$在$x<0$时等于$0$

所以不妨求$s(\frac{x}{INF} + INF \times s(x \times INF))$

如果$x<0$,括号内部的值趋向于$0$,得到的结果为$\frac{x}{4INF}+0.5$,那么取负、加上$0.5$、乘上$8INF$之后得到了$-2x$,加上$x$就得到了$-x$。

如果$x>0$,括号内部的值为$INF$,得到的结果为$1$。那么取负、加上$0.5$、乘上$8INF$、加上$x$之后得到了$4INF + x$,与我们需要的条件不符

那么我们只需要最后减去$4INF \times s(x \times INF)$即可满足条件了

当输入为$0$时会很尴尬地得到$0.5$的结果,我们只需要给$0$加上或减去$eps$就可以了

I
C 1 -0.0000000000000000000000001
< 2 100
S 3
< 4 102
> 1 100
+ 5 6
S 7
- 8
C 9 0.5
< 10 103
+ 1 11
+ 5 12
O 13

query 5

送分,模拟题,注意不要对$2^0$项左移即可

    freopen("nodes5.out" , "w" , stdout);
    queue < int > q;
    int cnt = 0;
    for(int i = 31 ; i >= 0 ; --i){
        puts("I");
        ++cnt;
        if(i)
            printf("< %d %d\n" , cnt++ , i);
        q.push(cnt);
    }
    while(q.size() > 1){
        printf("+ %d " , q.front());
        q.pop();
        printf("%d\n" , q.front());
        q.pop();
        q.push(++cnt);
    }
    printf("O %d\n" , q.front());

query 6

贼卡常

设$p$为当前数,$k$从$31$到$1$用$query\ 3$的方法检验$p-2^k$是否为正,如果为正减去$2^k$

$k=0$时可以直接输答案,无需判断。

考虑我们每一次使用$query\ 3$的方法时都需要乘上$INF$,常数太大,我们考虑在输入这个数据之后直接乘上$INF$,这样就不用每一次都左移。

使用浮点数计算二进制次幂。

可能会存在$p=2^k$,导致$s((p-2^k) \times INF)=0.5$的情况产生,我们只需要将$2^k -= eps$就不会有这种问题发生了。

    freopen("nodes6.out" , "w" , stdout);
    puts("I");
    puts("< 1 100");
    int cur = 2;
    for(int i = 131 ; i > 100 ; --i){
        printf("C %d -%.7lf\n" , cur , pow(2.0 , i-0.0001));
        printf("S %d\n" , cur + 1);
        printf("O %d\n" , cur + 2);
        printf("< %d %d\n" , cur + 3 , i);
        printf("- %d\n" , cur + 4);
        printf("+ %d %d\n" , cur , cur + 5);
        cur += 6;
    }
    printf("> %d 100\n" , cur);
    printf("O %d\n" , cur + 1);

query 7

实际上直接交样例可以在$Luogu$上$AC$,但是$UOJ\ Hack$了

用$query\ 6$的方法拆二进制,如果我们做按位加法,只有$11$不满足我们的要求

也就是说和为$2$时答案为$0$,否则和原来一样

设和为$x$,不难想到$ans=x +(-(s(x - 1.5) \times 2))$

    freopen("nodes7.out" , "w" , stdout);
    queue < int > que1 , que2 , que3;
    puts("I");
    puts("< 1 100");
    cur = 2;
    for(int i = 131 ; i > 100 ; --i){
        printf("C %d -%.7lf\n" , cur , pow(2.0 , i-0.0001));
        printf("S %d\n" , cur + 1);
        que1.push(cur + 2);
        printf("< %d %d\n" , cur + 2 , i);
        printf("- %d\n" , cur + 3);
        printf("+ %d %d\n" , cur , cur + 4);
        cur += 5;
    }
    printf("> %d 100\n" , cur);
    que1.push(cur + 1);
    puts("I");
    printf("< %d 100\n" , cur + 2);
    cur += 3;
    for(int i = 131 ; i > 100 ; --i){
        printf("C %d -%.7lf\n" , cur , pow(2.0 , i-0.00000000001));
        printf("S %d\n" , cur + 1);
        que2.push(cur + 2);
        printf("< %d %d\n" , cur + 2 , i);
        printf("- %d\n" , cur + 3);
        printf("+ %d %d\n" , cur , cur + 4);
        cur += 5;
    }
    printf("> %d 100\n" , cur);
    que2.push(cur + 1);
    ++cur;
    for(int i = 31 ; i >= 0 ; --i){
        printf("+ %d %d\n" , que1.front() , que2.front());
        ++cur;
        que1.pop();
        que2.pop();
        printf("C %d -1.5\n" , cur++);
        printf("< %d 200\n" , cur++);
        printf("S %d\n" , cur++);
        printf("< %d 1\n" , cur++);
        printf("- %d\n" , cur++);
        printf("+ %d %d\n" , cur - 5 , cur);
        ++cur;
        if(i)
            printf("< %d %d\n" , cur++ , i);
        que3.push(cur);
    }
    while(que3.size() > 1){
        printf("+ %d " , que3.front());
        que3.pop();
        printf("%d\n" , que3.front());
        que3.pop();
        que3.push(++cur);
    }
    printf("O %d\n" , que3.front());

query 8

考虑找到一个点$x0$使得$s'(x0)=0.1$

然后用跟$query\ 4$相似的方法,将$\frac{x}{INF}+x0$代入$s$就可以得到$0.1x0 + 0.1\frac{x}{INF}$

然后还原就可以了

关于这个$x0$我是直接蒯的数据可以二分求上面导数式子的零点

    freopen("nodes8.out" , "w" , stdout);
    puts("I");
    puts("> 1 96");
    puts("C 2 -0.9624236501192068949955178268487368462703686687713210393220363376803277");
    puts("S 3");
    puts("C 4 -0.2763932022500210303590826331268723764559381640388474275729102754589479");
    puts("< 5 95");
    puts("O 6");

query 9

使用冒泡排序(虽然我自己写的冒泡排序不知道怎么无限$WA$)

伪代码长这样:

for i 1 -> 16
    for j i+1 -> 16
        t = val[i] + val[j];
        val[j] = min(val[i]-val[j] , 0) + val[j];
        val[i] = t - val[j];

然后考虑如何实现上面的min函数

跟$query\ 4$的实现很像:求$s(\frac{x}{INF} + INF \times s(x \times INF))$,减$0.5$,乘$4INF$,减$s(x\times INF)\times 2INF$即可

    freopen("nodes9.out" , "w" , stdout);
    int num[20] = {};
    for(int i = 1 ; i <= 16 ; ++i){
        puts("I");
        num[i] = i;
    }
    cur = 16;
    for(int i = 1 ; i <= 16 ; ++i){
        for(int j = i + 1 ; j <= 16 ; ++j){
            printf("+ %d %d\n" , num[i] , num[j]);
            printf("- %d\n" , num[i]);
            printf("+ %d %d\n" , num[j] , cur + 2);
            printf("C %d 0.000000000000000000000000000001\n" , cur + 3);
            printf("< %d 500\n" , cur + 4);
            printf("S %d\n" , cur + 5);
            printf("< %d 151\n" , cur + 6);
            printf("> %d 150\n" , cur + 3);
            printf("+ %d %d\n" , cur + 7 , cur + 8);
            printf("S %d\n" , cur + 9);
            printf("- %d\n" , cur + 7);
            printf("C %d -0.5\n" , cur + 10);
            printf("< %d 152\n" , cur + 12);
            printf("+ %d %d\n" , cur + 11 , cur + 13);
            printf("+ %d %d\n" , num[i] , cur + 14);
            printf("- %d\n" , cur + 15);
            printf("+ %d %d\n" , cur + 1 , cur + 16);
            num[i] = cur + 15;
            num[j] = cur + 17;
            cur += 17;
        }
    }
    for(int i = 1 ; i <= 16 ; ++i)
        printf("O %d\n" , num[i]);

query 10

TBC

调$query\ 9$调太久了,不想写了,然后就随便蒯了一份代码把结果蒯了

尾声

这题目真养生~

转载于:https://www.cnblogs.com/Itst/p/10176270.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值