(Training 4)Codeforces Round #695
技術標籤:codeforces訓練演算法動態規劃
寒假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;
}
}