[2020多校联考]简单题

2020.11.23

解法

题目只要求求出 \(C\) 的值,它就提示我们 \(C\)\(A\)\(B\) 的值没有太大关系。照着这个思路我们把 \(A\) \(B\) 合并一下,搞成二元组。对 \(A+B\leq C\)\((A+B,C)\to(2(A+B),C-(A+B))\),对 \(A+B>C\)\((A+B,C)\to((A+B)-C,2C)\)。我们发现 \(AB\) 始终可以并在一起,干脆直接记为 \(D\)。对 \(D\leq C\)\((D,C)\to(2D,C-D)\),对 \(D>C\)\((D,C)\to(D-C,2C)\)。这两个式子有很大的共性,可以说成是对称的。

while(k--){
    if(a<=c) c-=a,a<<=1;
    else a-=c,c<<=1;
} 

(然而这波并没有任何的加成效果)

再观察,发现 \(2C\) 可以写成 \(C+C\),又左边是 \(D-C\),那么二元组内的和是没有变的,始终为 \(A+B+C\)。(这大概又提示我们和模运算有关?)

这个暴力不能优化的原因在于其中一条语句执行了一定次数,就会跳过去执行另一条语句。实际上是交换 \(A\)\(C\) 的值(因为式子是对称的)。大胆尝试,如果到了临界点不交换会发生什么,令 \(S=C+D\) ,那么 \(C+C=C+(S-D)=(C-D)+S\)\(D-C=D-(S-D)=2D-S\)。我们发现这交不交换的本质其实是一样的,只不过区别于有没有加上一个 \(S\)\(A+B+C\),所以可以一直乘 \(2\),然后对 \(S\) 取模即可。

#include<stdio.h>
#define ll long long
#define N 100007
 
inline int read(){
    int x=0,flag=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
    return flag? x:-x;
}
 
ll a,c,Mod;
int T,k;
 
ll qpow(ll x,ll y){
    ll ret=1,cnt=0;
    while(y>=(1LL<<cnt)){
        if(y&(1LL<<cnt)) ret=(ret*x)%Mod;
        x=(x*x)%Mod,cnt++;
    }
    return ret;
} 
 
int main(){
//  freopen("easy.in","r",stdin);
//  freopen("easy.out","w",stdout);
    T=read();
    while(T--){
        a=read()+read(),c=read(),k=read();
        Mod=a+c;
        printf("%lld\n",c*qpow(2LL,k)%Mod);
    }
}
posted @ 2020-11-23 15:09  Kreap  阅读(74)  评论(0编辑  收藏  举报