前言
開考,我大概 把這套題拍好,充分吸取 的教訓,所以每題都拍上了。
正文
T1
題目描述
在一個網格圖中,每次可以從
- 向上移動到 ;
- 向下移動到
- 向左移動到
- 向右移動到
求從 點出發,依此經過 的最短距離
分析
網格圖中的最短距離 曼哈頓距離
曼哈頓距離 行的絕對值 列的絕對值
代碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
int n,ans,x[100010],y[100010];
int main(){
read(n);
for(int i=1;i<=n;i++)read(x[i]);
for(int i=1;i<=n;i++)read(y[i]);
for(int i=1;i<=n;i++){
ans+=abs(x[i]-x[i-1])+abs(y[i]-y[i-1]);
}cout<<ans;
return 0;
}
T2
題目描述
給出 個人的名字和他們的 ,求這 個人的排名(第 個人的排名定義爲 比第 個人高的人數 )
分析
這題的答案跟名字沒有關係,而且數據範圍很小。
這樣的話,我們怎麼做都可以。
我的話是對這個數組進行排序,然後暴力查找這個最早出現在第幾個(因爲排名 比他高的人數 )
代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
int n,a[100010],b[100010];
string st[100010];
bool cmp(int a,int b){
return a>b;
}
int main(){
read(n);
for(int i=1;i<=n;i++)cin>>st[i];
for(int i=1;i<=n;i++)read(a[i]),b[i]=a[i];
sort(b+1,b+n+1,cmp);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)//暴力查找
if(b[j]==a[i]){
a[i]=j;
break;
}
for(int i=1;i<=n;i++)cout<<a[i]<<" ";
return 0;
}
T3
題目描述
有 個人 ,每個人有一個實力值。分別爲
你現在要把他們分成兩個隊伍,要求每個隊伍裏都得有人,並且使兩隊實力值之和的差最小。
分析
這題直接暴力找出所有方法就好了。
並沒有什麼技巧。
代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
int main(){
int a,b,c,d;
read(a);read(b);read(c);read(d);
int ans=INT_MAX;
ans=min(ans,abs((a)-(b+c+d)));
ans=min(ans,abs((b)-(a+c+d)));
ans=min(ans,abs((c)-(a+b+d)));
ans=min(ans,abs((d)-(a+b+c)));
ans=min(ans,abs((a+b)-(c+d)));
ans=min(ans,abs((a+c)-(b+d)));
ans=min(ans,abs((a+d)-(b+c)));
ans=min(ans,abs((b+c)-(a+d)));
ans=min(ans,abs((b+d)-(a+c)));
ans=min(ans,abs((c+d)-(a+b)));
cout<<ans;
return 0;
}
T4
題目描述
一個序列,你可以用一個單位的時間從頭部或尾部拿走一個數字,並得到這個數的值。你拿走這個數後,其他沒有被拿走的數字就會全部 。
分析
這題的話很顯然有一個結論,那就是喫的順序不會影響到答案。
這樣就簡單了,我們求遍和,再把該減的減去就行了。
代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
int n,x,s;
int main(){
read(n);
for(int i=1;i<=n;i++){
read(x);
s+=x;
}
cout<<s-(n-1)*n/2;
return 0;
}
T5
題目描述
子序列的定義:序列 是 的子序列,當且僅當從 中刪除若干個元素能得到 。
小 有兩個序列 ,,
要求你找到一個最長的序列c,滿足以下條件中的任何一個:
c是a的子序列但不是b的子序列;
c是b的子序列但不是a的子序列;
因爲出題人不會寫
spj
,所以就只要你輸出c的最長長度即可.
如果找不到,就輸出0.
分析
這題其實並不複雜。
- 如果兩個序列不完全相同,顯然答案 第 個序列的長度,第 個序列的長度
- 如果兩個序列完全相同,答案自然是 。
代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
int a[100010],b[100010];//見到 105 我就害怕
int main(){
int n,m;
read(n);
for(int i=1;i<=n;i++)read(a[i]);
read(m);
for(int i=1;i<=m;i++)read(b[i]);
if(n!=m)cout<<max(n,m);
else{
for(int i=1;i<=n;i++)
if(a[i]!=b[i]){
cout<<n;
return 0;
}
cout<<0;
}
return 0;
}
T6
題目描述
有 個石頭,第 個石頭的座標爲 ,不保證 有序。
你只能往前跳,並且你必須從 開始,中途踩到所有的石頭並最後跳到座標爲m的位置。
你有一個能力值 , 不是定值在一次跳躍中不會變化,但在一次跳躍中你每次能跳躍的距離不能大於你的能力值 。
有時候你可能跳不到石頭上,這時候你就會落到河裏.安全起見,你只能落水不超過 次。
求出爲了使落水不超過 次,你至少需要的能力值。
分析
這個直接二分 ,看落水次數是否 就行了。
代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
int a[100010],n,m,k;
bool check(int z){
int cishu=0;//拼音應該看的懂的吧
for(int i=2;i<=n;i++){
int x=a[i]-a[i-1]-1;
cishu+=x/z;
}
return cishu<=k;
}
int main(){
read(n);read(m);read(k);
n++;//a[1]=0;
for(int i=2;i<=n;i++)read(a[i]);
a[++n]=m;//一點點的小技巧
sort(a+1,a+n+1);
int l=-1,r=INT_MAX;
while(l+1<r){
int mid=(l+r)>>1;
if(check(mid))r=mid;
else l=mid;
}cout<<r;
return 0;
}
T7
題目描述
有一個長爲 的序列 。
記 ,即 爲序列中第 個數到第 個數的最大值和最小值之差。
求出對於所有的滿足 的 的 之和。
分析
萬歲!智商不夠,數據結構來湊。
我的這種做法很不要動腦子,我暫時很沒找到別的做法。
算法
簡單講講
又稱 表,可以實現 靜態區間查詢最大或最小值,線段樹的話會多一個 。並且這種算法初始化的時間複雜度也是非常優秀的—— 。
這個東西如何實現呢?這個東西本質上就是一個倍增。
定義 表示第 個數中最小的。
學過倍增的同學,這個遞推式應該很簡單就能推出來。
重點講查找,其實上面的內容可能不足爲奇,但是查找這部分確實有技術含量了。
首先,設一個數爲
對於任意數,一定可以找到 滿足以下條件:
- 這個數
- 這個數
沒有理解也沒關係,我們來看這個算法到達是怎麼實現的
這個圖應該還是滿直觀的
代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
ll f1[17][100010],f2[17][100010],b[100010],a[100010],n,ans;
void bulid(){
for(int j=1;j<=16;j++)
for(int i=1;i<=n;i++)
f1[j][i]=max(f1[j-1][i],f1[j-1][i+(1<<(j-1))]);
for(int j=1;j<=16;j++)
for(int i=1;i<=n;i++)
f2[j][i]=min(f2[j-1][i],f2[j-1][i+(1<<(j-1))]);
}//RMQ
int Max(int l,int r){
int len=r-l+1;
return max(f1[b[len]][l],f1[b[len]][r+1-(1<<b[len])]);
}
int Min(int l,int r){
int len=r-l+1;
return min(f2[b[len]][l],f2[b[len]][r+1-(1<<b[len])]);
}
int main(){
read(n);
for(int i=1;i<=n;i++)read(a[i]);
for(int i=1;i<=n;i++)f1[0][i]=a[i];//RMQ初始化
for(int i=1;i<=n;i++)f2[0][i]=a[i];//RMQ初始化
bulid();
for(int i=1;i<=16;i++)b[1<<i]++;
for(int i=1;i<=n;i++)b[i]+=b[i-1];
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
ans+=Max(i,j)-Min(i,j);
cout<<ans;
return 0;
}
T8
題目描述
有一個長爲 的序列 ,保證序列裏的數字都是 或 。
記 爲關於整數 的函數。
- 當 爲奇數時 ;
- 當 爲偶數時 。
記 。
記 (所有滿足 的 之和)
有 次詢問,每次給你一個 ,要你求出 的值。
分析
找規律
這道題我們先不要管 。
我們先來看看 中每個數在所有 中 的區間中被計算了多少次。(本來其實是希望用差分序列找通項式的,結果有意外的驚喜)
先來寫個程序
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
int n,a[100010];
int main(){
read(n);
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++)
for(int k=i;k<=j;k++)
a[k]++;
for(int i=1;i<=n;i++)cout<<a[i]<<" ";
return 0;
}
我們來試試不同 的值會對計算次數產生什麼影響。
- 當 時,程序中的 序列爲
1
; - 當 時,程序中的 序列爲
2 2
; - 當 時,程序中的 序列爲
3 4 3
; - 當 時,程序中的 序列爲
4 6 6 4
; - 當 時,程序中的 序列爲
5 8 9 8 5
; - 當 時,程序中的 序列爲
6 10 12 12 10 6
; - 當 時,程序中的 序列爲
7 12 15 16 15 12 7
。
這個時候我們再來關注一下 的餘數
- 當 時,程序中的 序列爲
1
; - 當 時,程序中的 序列爲
0 0
; - 當 時,程序中的 序列爲
1 0 1
; - 當 時,程序中的 序列爲
0 0 0 0
; - 當 時,程序中的 序列爲
1 0 1 0 1
; - 當 時,程序中的 序列爲
0 0 0 0 0 0
; - 當 時,程序中的 序列爲
1 0 1 0 1 0 1
。
規律已經很明顯了
- 當 爲偶數的時候,全部都爲 ;
- 當 爲奇數的時候,一個 一個 間隔開來的。
所以
- 當詢問區間長度爲偶數時,直接輸出 ;
- 當詢問區間長度爲奇數是,答案爲
前綴和
如何求出 呢?
我們可以用上前綴和。
我們這樣求出
for(int i=1;i<=n;i++)s[i]=s[i-2]+a[i];//似乎RE一點點沒關係
這樣就好辦了,我們直接去解決詢問了。
while(T--){
int l,r;
read(l);read(r);
if((r-l+1)%2==0)puts("0");//區間的長度爲偶數
else{
int ans=(s[r]-s[l-2])%2;
printf("%d\n",ans);
}
}
代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
int a[100010],s[100010],n,T;
int main(){
read(n);read(T);
for(int i=1;i<=n;i++)read(a[i]);
for(int i=1;i<=n;i++)s[i]=s[i-2]+a[i];
while(T--){
int l,r;
read(l);read(r);
if((r-l+1)%2==0)puts("0");//區間的長度爲偶數
else{
int ans=(s[r]-s[l-2])%2;
printf("%d\n",ans);
}
}
return 0;
}
後記
這場比賽也正是檢查的好,只有有這個習慣,才能保證該有的分數能全部拿到。
到目前爲止,還有一點點的遺憾, 我確乎不會對那個規律進行證明,繼續思考吧!