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,答案为Niai之和
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;
}