~这个队伍第一次组队赛是ccpc网赛,我全场梦游,由于我菜得抠脚,so导致打得跟什么一样,。。
然后这波cf是第二次组队赛,感觉还可以,虽然我...再次全场梦游,感觉自己一打组队赛就很迷,总是会想跟队友讨论,看来有时候自己写一发会靠谱些
就F、G、K题有意义,其他的队友都版切了。
给一个冰箱,然后需要拿n个数字为ai的蛋糕,可以放k种蛋糕在冰箱外(下次可以直接拿这种不用开冰箱了),问最少需要打开冰箱多少次。
很显然的得出一个贪心的结论:对于外面这k个蛋糕,当遇到一个外面没有放有的蛋糕,应该把这k个蛋糕从这以后各个蛋糕第一次出现的位子中最后那一个放进冰箱中(因为如果放其他的,都会比这个先再次需要拿取那么在这个点的时候拿蛋糕)
那么就需要一个方法存放在外面的蛋糕里哪个后面第一次出现的位子最晚,这里可以用set维护一个结构体存这个蛋糕的下标,和下一次出现的位子即可,然后需要一个预处理每个蛋糕在每个点下一次出现的位子。
这里有个set的坑,需要multiset来解决或者最后出现的位置不是1e9+7而改为1e9+i。
这里set对于一个结构体如果按照b排序,如果a不同,即便放入多个结果还是只有1个,而如果按a排序,a不同就可以存多个。
G、待补
K. Two Subarrays
这里很明显是2个子串一个在左边,一个在右边。那么我们可以考虑找到这么一个分界线,这个答案就是这个分界线max(左边最大值-右边最大值,左边最小值-右边最小值,左边最大值-右边最小值,左边最小值-右边最大值)这里全部是用绝对值。
有了这个思想那么就是如何每个分界线找左边和右边的最大与最小值。
然后这里就想到用dp去做
dp1[i][j][k] 从左边开始扫,j和k都只有0和1,j=1表示最大值,j=0表示最小值,k=0表示这个数字此时是取-,k=1表示这个数字此时是取+。
那么dp1[i][1][1]意思是以i结尾且这个数字取+的最大值
同理dp1[i][0][1]意思是以i结尾且这个数字取+的最小值
那么可以得到dp1的转移方程是
dp1[i][1][1]=max(qq[i],dp1[i-1][1][0]+qq[i]);//Ҫ
dp1[i][1][0]=dp1[i-1][1][1]-qq[i];
dp1[i][0][1]=min(qq[i],dp1[i-1][0][0]+qq[i]);
dp1[i][0][0]=dp1[i-1][0][1]-qq[i];
然后用lm1[i]表示从左边开始到i位子可以取到的最大值
然后用lm2[i]表示从左边开始到i位子可以取到的最小值
这里
lm1[i]=max(dp1[i][1][1],dp1[i][1][0]);
lm2[i]=min(dp1[i][0][1],dp1[i][0][0]);
dp2[i][j][k] 从右边开始扫,j和k都只有0和1,j=1表示最大值,j=0表示最小值,k=0表示这个数字此时是取-,k=1表示这个数字此时是取+。
转移方程是
dp2[i][1][1]=max(qq[i],dp2[i+1][1][0]+qq[i]);//Ҫ
dp2[i][1][0]=max(-qq[i],dp2[i+1][1][1]-qq[i]);
dp2[i][0][1]=min(qq[i],dp2[i+1][0][0]+qq[i]);
dp2[i][0][0]=min(-qq[i],dp2[i+1][0][1]-qq[i]);
rm1[i]=dp2[i][1][1]; //因为是从后面开始,其最大值只能从这点为正开始取得
rm2[i]=dp2[i][0][1];
然后最后就枚举每个线求两边最大的差值取最大值即可,下面是AC代码
#include <iostream>
#include <stdio.h>
#include <vector>
#include <string.h>
#include <algorithm>
using namespace std;
const int maxn=1e5+7;
typedef long long ll;
ll dp1[maxn][2][2];
ll dp2[maxn][2][2];
ll qq[maxn];
ll lm1[maxn];
ll lm2[maxn];
ll rm1[maxn];
ll rm2[maxn];
int main(){
int i,j,k,f1,f2,f3,f4,t1,t2,t3,t4;
//freopen("in.txt","r",stdin);
//freopen("out1.txt","w",stdout);
int n,m,T;
long long g1,g2,g3,g4,sum1,sum2,sum3;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%I64d",&qq[i]);
memset(dp1,0,sizeof(dp1));memset(dp2,0,sizeof(dp2));
memset(lm1,0,sizeof(lm1));memset(lm2,0,sizeof(lm2));
memset(rm1,0,sizeof(rm1));memset(rm2,0,sizeof(rm2));
dp1[1][1][1]=dp1[1][0][1]=qq[1];
dp1[2][1][1]=dp1[2][0][1]=qq[2]; //表示只要第二个
dp1[2][1][0]=dp1[2][0][0]=qq[1]-qq[2]; //不可以不选,说明是没有0的
lm1[1]=lm2[1]=qq[1];
lm1[2]=max(lm1[1],qq[2]);
lm1[2]=max(lm1[2],qq[1]-qq[2]); //最大值
lm2[2]=min(lm2[1],qq[2]);
lm2[2]=min(lm2[2],qq[1]-qq[2]);
for(i=3;i<=n;i++){
dp1[i][1][1]=max(qq[i],dp1[i-1][1][0]+qq[i]);//Ҫ
dp1[i][1][0]=dp1[i-1][1][1]-qq[i];
dp1[i][0][1]=min(qq[i],dp1[i-1][0][0]+qq[i]);
dp1[i][0][0]=dp1[i-1][0][1]-qq[i];
lm1[i]=max(dp1[i][1][1],dp1[i][1][0]);
lm2[i]=min(dp1[i][0][1],dp1[i][0][0]);
}
for(i=3;i<=n;i++){
lm1[i]=max(lm1[i],lm1[i-1]);
lm2[i]=min(lm2[i],lm2[i-1]);
}
dp2[n][1][1]=dp2[n][0][1]=qq[n];
dp2[n-1][1][1]=dp2[n-1][0][1]=qq[n-1];
dp2[n-1][1][1]=max(dp2[n-1][1][1],qq[n-1]-qq[n]);
dp2[n-1][0][1]=min(dp2[n-1][0][1],qq[n-1]-qq[n]);
dp2[n-1][0][0]=dp2[n-1][1][0]=-qq[n-1];
dp2[n-1][0][0]=min(dp2[n-1][0][0],-qq[n-1]+qq[n]);
dp2[n-1][1][0]=max(dp2[n-1][1][0],-qq[n-1]+qq[n]);
rm1[n]=rm2[n]=qq[n];
rm1[n-1]=max(rm1[n],qq[n-1]);
rm1[n-1]=max(rm1[n-1],qq[n-1]-qq[n]);
rm2[n-1]=min(rm2[n],qq[n-1]);
rm2[n-1]=min(rm2[n-1],qq[n-1]-qq[n]);
for(i=n-2;i>=1;i--){
dp2[i][1][1]=max(qq[i],dp2[i+1][1][0]+qq[i]);//Ҫ
dp2[i][1][0]=max(-qq[i],dp2[i+1][1][1]-qq[i]);
dp2[i][0][1]=min(qq[i],dp2[i+1][0][0]+qq[i]);
dp2[i][0][0]=min(-qq[i],dp2[i+1][0][1]-qq[i]);
rm1[i]=dp2[i][1][1]; //因为是从后面开始,其最大值只能从这点为正开始取得
rm2[i]=dp2[i][0][1];
}
for(i=n-2;i>=1;i--){
rm1[i]=max(rm1[i],rm1[i+1]);
rm2[i]=min(rm2[i],rm2[i+1]);
}
sum1=0;
for(i=1;i<n;i++){
sum1=max(sum1,abs(lm1[i]-rm1[i+1]));
sum1=max(sum1,abs(lm1[i]-rm2[i+1]));
sum1=max(sum1,abs(lm2[i]-rm1[i+1]));
sum1=max(sum1,abs(lm2[i]-rm2[i+1]));
}
printf("%I64d\n",sum1);
}
return 0;
}