iwtgm-11

题目链接

A.

能全买,就让剩余的总钱/全买,加上可得的糖数,总钱-这些花费
此时不能全买,就遍历一遍,算出能买的总数
再让剩余的总钱/能买的...

这样是不会T的:
设total为这一轮能买的糖果的总价格,last为之前剩下的钱,now为这一轮买完糖果后剩下的钱
now=last%total,所以now<total,(取模肯定小于模数)
now<last-total,(不然还能买),
所以now<last/2
所以是log(钱初始总数)的时间复杂度

自己写的时候还加了树状数组求前缀和,不仅没优化还T了

ll n,total,res,sum,cnt;
ll a[N];
void solve(){
    cin>>n>>total;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        if(total-sum>=a[i]){
            sum+=a[i];cnt++;
        }
    }
    while(sum>0&&total>0){
        res+=total/sum*cnt;
        total%=sum;
        sum=0;cnt=0;
        for(int i=1;i<=n;i++){
            if(total-sum>=a[i]){
                sum+=a[i];cnt++;
            }
        }
    }
    cout<<res;
}

B.

把一种可能的路径情况分为两部分,
蛇形和环形,两者分开独立计算,环形又分为起点、箭头和环身
枚举蛇形的列数


看代码注释:

init_b(){//算蛇形
    //a1[]是第一行的蘑菇生长速度,a2[]是第二行的蘑菇生长速度
    int s=0;//相当于前缀和
    for(int i=1;i<=n;i+=2){//这里是两列两列算的,有其他方法能求出这个前缀也行
        s+=a1[i]*(i*2-2);//蘑菇生长速度*秒数
        s+=a2[i]*(i*2-1);//蘑菇生长速度*秒数
        b[i]=s;//走到当前列一共能采的蘑菇数
        s+=a2[i+1]*(i*2);//两列中的第二列
        s+=a1[i+1]*(i*2+1);
        b[i+1]=s;
    }
}//预处理出了从出发到当前列i一直是走蛇形所采的蘑菇数
init_S(){这里没有算秒数,只是生长速度的后缀和,继续往下看
    int s=0;//后缀和
    for(int i=n;i>=1;i--)
    s+=a1[i]+a2[i],S[n-i+1]=s;//从右往左数的S1,S2...
}
init_C1(){
    //从上面绕下去,起点在上面
    //c1[i]表示从第i列(从左往右数)开始的 
    int s=0;
    for(int i=n,l=0;i>=1;i--,l++){
        s+=S[l];//l是右边的列数,每次加的是后缀和(也就是越靠右的加的次数越多,这样秒数就体现出来了)
        s+=a2[n-l]*(l+l+1);//算的是箭头的那一格,先算的箭头,再加后缀和,从右向左的方向,那么第一行需要多的秒数也体现出来了
        c1[i]=s;//实际上这个值就是整个环形的总蘑菇数
    } 
}
init_C2(){
    //从下面绕上去
    //c2[i]表示从第i列(从左往右数)开始的 
    int s=0;
    for(int i=n,l=0;i>=1;i--,l++){
        s+=S[l];
        s+=a1[n-l]*(l+l+1);//只有这里的a1,a2换了一下 
        c2[i]=s;
    } 
}
//枚举从哪开始拐,开头看作是0,偶数向下,然后下一列就向右延伸,那么箭头位置就在第一行,是c2[]
    for(int i=0;i<=n;i++){
        //从下一列开始拐弯 
        if(i%2) ans=max(ans,b[i]+c2[i+1]+S[n-i]*(i*2));//当前列数是奇数,向上,在第一行的时候向右延伸,箭头位置在第二行,是c1[]
        else    ans=max(ans,b[i]+c1[i+1]+S[n-i]*(i*2));//因为我们是把环独立算的,事实上起点不是0,前面蛇形已经走了一些秒数,那么把环形整个提升提升i*2的秒数
    }
//主函数
#define int long long
int n,ans,a1[N],a2[N],S[N],b[N],c1[N],c2[N];
signed main(){
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a1[i];
    for(int i=1;i<=n;i++)cin>>a2[i];
    init_S();
    init_b();
    init_C1();
    init_C2();
    for(int i=0;i<=n;i++){
        if(i%2) ans=max(ans,b[i]+c2[i+1]+S[n-i]*(i*2));
        else    ans=max(ans,b[i]+c1[i+1]+S[n-i]*(i*2));
    }
    cout<<ans;

C.

期望是均值,所有情况总疲惫值之和/所有情况=期望,题目求期望所有情况就是求所有情况的总疲惫值之和
可以分开算每个疲惫值出现的次数
疲惫值之和
每个点都可以是休息点或不是休息点,那么就有2^(n-1)种情况
考虑每个疲惫值出现的次数,
如a[1],1号点一定是a[1],那么个数有2^(n-1),
然后后面的(n-1)个点每一个点都可以是a[1],固定一个点取a[1]后,剩下的(n-2)个点(可以取a[1]或者接着前一个的疲惫),有2(n-2)种情况,这些情况里都包括了这个a[1],都要算一个次数,然后固定点的取值范围有n-1个,就是(n-1)*2(n-2)。
注意:重复的是休息点的状态,但是我们计算的是这个固定点的次数,有多少种情况这个固定点就要加多少次
如果一种状态里有两个a[1],也就是会有两个固定点,一个固定点计算这种状态时只加了它自己的一个,所以状态重复没有关系

对于a[2],一定要连续的两个点,就是说有两个点是固定的,(1、2),开头选了1,2两个点,剩下的n-2个点任取,有2^(n-2)。
1号点永远是a[1]不变,剩n-1个点,两个连续的点有n-2组,固定一组,剩n-3个点任取,就是2(n-2)+(n-2)*2(n-3)

推广:a[i]的个数为2^(n-i)+(n- i)2^(n-i-1)
设a[i]的个数为Ni,答案为Ni
ai之和

ll p=998244353;
ll a[N],pw[N];
void init(){
    pw[0]=1;
    for(int i=1;i<N;i++){
        pw[i]=pw[i-1]*2%p;
    }
}
void solve(){
    ll n;cin>>n;
    init();
    for(int i=1;i<=n;i++)cin>>a[i];
    ll ans=0;
    for(int i=1;i<=n;i++){
        ll tmp=(pw[n-i]+(n-i)*pw[n-i-1])%p;
        tmp=(tmp*a[i])%p;
        ans=(ans+tmp)%p;
        //ans=add(ans,add(pw[n-i],(n-i)*pw[n-i-1]%p)*a[i]%p);
    }
    cout<<ans;
}
posted @ 2023-11-03 15:01  WW爆米花  阅读(8)  评论(0编辑  收藏  举报