A 門牌號
題意:統計 \([1,2020]\) 所有整數中,數碼 \(2\) 出現的次數
分析:直接暴力枚舉即可:
inline int f(int n){
int ans=0;
while(n) ans+=(n%10==2),n/=10;
return ans;
}
...
int ans=0;
for(int i=1;i<=2020;i++) ans+=f(i);
輸出為 \(624\)
B 既約分數
題意:對於滿足 \(\displaystyle a,b\in Z,{a\over b}\) 為最簡分數的分數稱為既約分數,求 \(1\leq a,b\leq 2020\) 的既約分數個數
分析:暴力枚舉 \(a,b\) ,用 gcd 判是否互質
int ans=0;
for(int a=1;a<=2020;a++)
for(int b=1;b<=2020;b++)
ans+=(__gcd(i,j)==1);
輸出為 \(2481215\)
C 蛇形矩陣
題意:給出如下矩陣,求第 \(20\) 行 \(20\) 列
1 2 6 7 ...
3 5 8 ...
4 9 ...
10 ...
...
分析:考慮令 \(f_n\) 為第 \(n\) 行 \(n\) 列的值
不難看出,\(f_n\) 轉移至 \(f_{n+1}\) 需要自己所在斜線走一半+下一條斜線走完+\(f_{n+1}\) 所在斜線走一半
根據各斜線的長度,不難列出轉移方程 \(f_{n+1}=f_n+n-1+2n+n+1=f_n+4n\) ,可解出 \(\displaystyle f_n=4\sum_{i=1}^{n-1}i+f_1=2n(n-1)+1\)
代入 \(n=20\) 得到 \(f_{20}=761\)
D 七段碼
題意:求多少種不同的七段碼表示方法,各個亮起的燈管互相連接,且至少有一個燈管亮起
分析:用數組儲存燈管間的連接情況,枚舉 \(2^7\) 種情況,用 bfs 檢查是否滿足,統計答案即可
#include<bits/stdc++.h>
using namespace std;
bool connect[7][7]={
{0,1,0,0,0,1,0},
{1,0,1,0,0,0,1},
{0,1,0,1,0,0,1},
{0,0,1,0,1,0,0},
{0,0,0,1,0,1,1},
{1,0,0,0,1,0,1},
{0,1,1,0,1,1,0}
};
inline int isconnect(int S){
int que[16],Head=0,Tail=0,NS=0;
bool vis[16]={0};
for(int i=0;i<7;i++) if( (S>>i)&1 ){
que[++Tail]=i;
vis[i]=1;
break;
}
while(Head<Tail){
int now=que[++Head];
NS|=1<<now;
for(int i=0;i<7;i++)
if( ((S>>i)&1)&&connect[now][i]&&!vis[i] ){
que[++Tail]=i;
vis[i]=1;
}
}
return NS==S;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int Ans=0;
for(int i=1;!(i>>7);i++) Ans+=isconnect(i);
cout<<Ans;
cout.flush();
return 0;
}
輸出為 \(80\)
E 直線與圓
題意:用 \(20\) 條直線與 \(20\) 個圓,求最多能將平面分為幾個部分
分析:任意線(包括直線與曲線)之間,不交於同一點,則能將平面分為盡可能多的部分
根據歐拉定理 \(V-E+F-T=1,T=0\) ,求解出 \(F=1+E-V\) ,只需求解出點數 \(V\) 與邊數 \(V\) 即可求解
根據我們的分析,任意兩直線即可有一交點,個數為 \(\displaystyle (^{20}_2)\) ;任意兩圓之間有兩個交點,個數為 \(\displaystyle 2\cdot (^{20}_2)\) ;任意圓與直線之間有兩個交點,個數為 \(20\times 20\times 2\)
故 \(\displaystyle V=(^{20}_2)+2\cdot (^{20}_2)+20\times 20\times 2=1370\)
同樣根據我們的分析,一條直線被剩余的 \(19\) 條直線各交於一點,被 \(20\) 個圓各交於兩點,故線上有 \(19+20\times 2=59\) 個點,一條直線被分為 \(60\) 條邊;一個圓被剩余的 \(19\) 個圓、 \(20\) 條直線各交於兩點,共 \((19+20)\times 2=78\) 個點,一個圓被分為 \(78\) 條邊
故 \(\displaystyle E=60\times 20+78\times 20=2760\)
因此得到答案: \(\displaystyle F=1+2760-1370=1391\)
F 成績分析
題意:給出 \(n\) 個學生的成績,求最高分、最低分與平均值
分析:直接在輸入的時候儲存最大值、最小值和分數和,最后分數和除去人數得到平均分
#include<iostream>
#include<iomanip>
using namespace std;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int N,S,MinS,MaxS,SumS;
cin>>N>>S;
MinS=MaxS=SumS=S;
for(int i=2;i<=N;i++)
cin>>S,MinS=min(MinS,S),MaxS=max(MaxS,S),SumS+=S;
cout<<MaxS<<'\n'<<MinS<<'\n'<<fixed<<setprecision(2)<<SumS*1.0/N;
cout.flush();
return 0;
}
G 回文年份
題意:輸入一個日期,輸出嚴格后於日期的第一個 ABCDDCBA 型日期,和第一個 ABABBABA 型日期。保證有解。
分析:第一種日期,直接枚舉對應月份的對應日期,進行翻轉,儲存好 \(366\) 個日期后排序,查找第一個比輸入大的即可(數據量太小,懶得寫二分了)。第二種日期,由於月份和日期相同,直接枚舉月份,進行翻轉,儲存好 \(12\) 個日期后和第一個儲存方法相同。
#include<iostream>
#include<algorithm>
using namespace std;
int Day1[512],Day2[512];
inline int built(int month,int day){
return ((day%10*10+day/10)*100+(month%10*10+month/10))*10000+month*100+day;
}
inline void pre(){
int cntday[]={0,31,29,31,30,31,30,31,31,30,31,30,31},CntDay1=0;
for(int month=1;month<=12;month++)
for(int day=1;day<=cntday[month];day++)
Day1[++CntDay1]=built(month,day);
sort(Day1+1,Day1+367);
for(int month=1;month<=12;month++)
Day2[month]=built(month,month);
sort(Day2+1,Day2+13);
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
pre();
int N;
cin>>N;
for(int i=1;i<=366;i++) if(Day1[i]>N) {
cout<<Day1[i]<<'\n';
break;
}
for(int i=1;i<=12;i++) if(Day2[i]>N) {
cout<<Day2[i]<<'\n';
break;
}
cout.flush();
return 0;
}
H 子串序列
題意:求一個字符串 \(S\) 的所有子串中,每個子串出現僅一次的字符個數和
分析:對於每一個字符單獨考慮:對於第 \(i\) 個字符 \(c\) ,設其前面第一個與之相同的字符出現的位置為 \(pre_i\) (沒有則為 \(0\)),后面第一個為 \(nxt_i\) (沒有則為 \(n+1\))
則對於滿足 \(l\in(pre_i,i],r\in [i,nxt_i)\) 的所有區間 \([l,r]\) ,僅出現一個字符 \(c\) ,故對每個區間都產生 \(1\) 的貢獻,總貢獻即為 \((i-pre_i)\cdot (nxt_i-i)\) ,每個統計起來即可
#include<iostream>
#include<string>
using namespace std;
typedef long long ll;
const int MAXN=1e5+10;
string s;
int Pre[MAXN],Nxt[MAXN],PreIndex[26];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
getline(cin,s);
for(int i=0;i<s.size();i++){
if(PreIndex[s[i]-'a']) Nxt[ PreIndex[s[i]-'a'] ]=i+1;
Pre[i+1]=PreIndex[s[i]-'a'];
Nxt[i+1]=s.size()+1;
PreIndex[s[i]-'a']=i+1;
}
ll Ans=0;
for(int i=1;i<=s.size();i++) Ans+=1ll*(i-Pre[i])*(Nxt[i]-i);
cout<<Ans;
cout.flush();
return 0;
}
I 荒島探測
題意:給定一個三角形荒島,在荒島上的某個人距離兩給定點的距離之和不得超過給定值 \(L\) ,問其可覆蓋的面積大小
分析:(口胡算法,現場未實現)
動點到兩頂點距離之和不超過某定值,圖形為一橢圓。問題等價於給定橢圓與給定三角形的交集圖形大小。
在橢圓上逆時針順序取 \(10^6\) 個點,按序連接,構成 \(10^6\) 個向量。將這些向量與三角形的三個向量求解半平面交,若有交集,則輸出大小;否則輸出 \(0\)
J 字串排序
題意:求解一字符串,滿足條件:按照冒泡排序算法,需要交換的次數為給定次數 \(V\) ;若有多解,輸出最短的;若仍有多傑,輸出字典序最小的
分析:待補