寒假vp的第一场cf 这场好像也有点难度
A. Wizard of Orz
题意:只能按下一次停止 按下后会开始往左右两边开始逐渐开始停止 问在某个位置按下后 所能得到的值最大是多少
思路:一个简单的构造 首先我们应该知道应该时让首位为9是最好的 那么我们可以看 如果按下首位的话 那么第二位为0 再看按下第二位 那么要使得首位为9 在第二为为8时按下 这时候首位9 第3位也为9 这时候如果从第3为按下则第3为为7 前2位为9 8 所以从第二位为8时按下应该是最大值
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e6+10;
int main(){
int T;
cin>>T;
while(T--){
int n,x=8;
scanf("%d",&n);
printf("9");
for(int i=2;i<=n;i++){
printf("%d",x);
x++;
x=x%10;
}
puts("");
}
}
B. Hills And Valleys
题意:如果a[i]<a[i-1]&&a[i]<a[i+1]那么被称为山谷
a[i]>a[i-1]&&a[i]<a[i+1]那么被称为山峰
问能且只能对一个a[i]进行修改 这时候的山峰山谷的数量和最小值是多少
思路:从前往后统计山峰山谷的次数总和 很容易想到 如果将一个数变为前一个或者变为后一个那么一定会使得山峰山谷的数量和小于等于 然后从前往后进行枚举 将每一位进行修改 求出能减少的最大值 用第一遍的次数从何减去能减少的最大值 即为答案
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e6+10;
int a[N],n;
int check(int i){
if(i==n||i==1)return 1;
return (a[i-1]<a[i]&&a[i]>a[i+1])||(a[i-1]>a[i]&&a[i]<a[i+1]);
}
int main(){
int T;
cin>>T;
while(T--){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
int ans=0;
for(int i=2;i<n;i++)
ans+=check(i);
int ans1,ans2,d=0;//变化的最大值
for(int i=2;i<=n-1;i++){
ans1=0;
ans2=0;
ans1+=check(i-1);
ans1+=check(i);
ans1+=check(i+1);
int t=a[i];
a[i]=a[i-1];
ans2+=check(i-1);
ans2+=check(i);
ans2+=check(i+1);
d=max(ans1-ans2,d);
ans2=0;
a[i]=a[i+1];
ans2+=check(i-1);
ans2+=check(i);
ans2+=check(i+1);
d=max(ans1-ans2,d);
a[i]=t;
}
cout<<ans-d;
puts("");
}
}
另一种是我一开始错了的 我可能细节上处理出问题了 如果是有连续山峰山谷山峰或连续山谷山峰山谷 那么一定存在改变一个-3(我们将中间的改为最低或者最高的那个) 如果山峰山谷这种2个连续的一定存在-2 如果是不存在连续那么 -1 我们取最小值 相加然后就为答案
(如果错了请看到的聚聚告诉我一下)
C. Three Bags
题意:有3个背包 有一个操作 列如 在1背包我们取出a 2背包取出b 然后放入a背包的a-b 直到只有一个背包有一个数 然后另外2个背包 一个数都没
求出这个数的最大值
思路:很好想 我们用最小值做牺牲作为中介 让他把另一个背包转换到一个背包 那么我们需要2个这样的数 将所有别的数全部转换到一起去 所以 这里我们取3个背包中3个最小值 的2个最小值 还有一种情况就是用一个背包来做中介 他这一个背包都全部取负 在这里我们用2种情况最小值
记住要开LL vp时没开longlong都不知道是怎么错的
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e6+10,INF=0x3f3f3f3f;
int a[N],n;
int main(){
int n1,n2,n3;
long long min1=INF,min2=INF,min3=INF,minsum,sum=0,x,sum1=0,sum2=0,sum3=0;
scanf("%d%d%d",&n1,&n2,&n3);
for(int i=1;i<=n1;i++){
scanf("%lld",&x);
sum+=x;
sum1+=x;
min1=min(x,min1);
}
for(int i=1;i<=n2;i++){
scanf("%lld",&x);
sum+=x;
min2=min(x,min2);
sum2+=x;
}
for(int i=1;i<=n3;i++){
scanf("%lld",&x);
sum+=x;
min3=min(x,min3);sum3+=x;
}
minsum=min(min1+min2,min(min2+min3,min3+min1));
cout<<sum-2*min(minsum,min(sum1,min(sum3,sum2)));
}
D. Sum of Paths
题意 一个机器人 有n个位置 他能走k次 起点随意 要刚好走k步 称之为良好;路径 良好的权值为他每次走过的点的权值相加(重复走的点会多次加)
问所有可能的良好路径所产生的总权值为多少 m次询问 修改第x个点为v后的总权值为多少
思路:我们考虑每个点的贡献 即为每个点会经过的总次数 总权值就为所有点的次数*权值
我们取f[i][j]为走第i次时结尾为j的方案数 易得转移方程为
f[i][j]=f[i-1][j+1]=f[i-1][j-1]这个我们求出的是方案数而不是总次数
因为起点终点我们可以逆向看 所以总次数tot[j]= f[i][j]*f[k-i][j]的值i从0到k
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=6e3+10,mod=1e9+7;
int a[N];
int f[N][N];
int tot[N];
typedef long long LL ;
int main(){
int n,k,m;
scanf("%d%d%d",&n,&k,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
f[0][i]=1;
}
for(int i=1;i<=k;i++){
for(int j=1;j<=n;j++){
if(j!=1)
f[i][j]=(f[i][j]+f[i-1][j-1])%mod;
if(j!=n)
f[i][j]=(f[i][j]+f[i-1][j+1])%mod;
}
}
LL ans=0;
for(int i=0;i<=k;i++)
for(int j=1;j<=n;j++){
tot[j]=(tot[j]+1ll*f[i][j]*f[k-i][j]%mod)%mod;
}
for(int i=1;i<=n;i++)
ans=(ans+1ll*tot[i]*a[i]%mod)%mod;
//cout<<ans<<endl;
while(m--){
int x,v;
scanf("%d%d",&x,&v);
ans=(ans-1ll*tot[x]*a[x]%mod+1ll*v*tot[x]%mod+mod)%mod;
a[x]=v;
cout<<ans<<endl;
}
}