題目鏈接:https://www.jisuanke.com/contest/6516
A:題目:
我們稱一個數是質數,而且數位中出現了 5 的數字是有趣的。
例如 5, 59, 457。求1到100000中有趣的數的個數。
題解:無腦分解和暴力枚舉素數即可。
代碼:
#include <algorithm> #include <cstdio> #include <cmath> #include <cstring> #include <iostream>
using namespace std; typedef long long ll; const int mod = 10007; bool check(int x) { int flag = 0; for (int i = 2; i <= sqrt(x); i++) if (x % i == 0) flag = 1; if (flag == 1)return false; else return true; } bool check2(int x) { int t; while (x != 0) { t = x % 10; x = x / 10; if (t == 5)return true; } return false; } int main(void) { int num = 0; for (int i = 2; i <= 100000; i++) { if (check(i) == true) if (check2(i) == true) num++; } cout << num << endl; return 0; }
答案:3282
B:題目:
蒜頭君要爬樓梯。樓梯一共有10層台階。
因為腿長的限制,每次最多能上4層台階。但是第5,7層樓梯壞掉了不能踩。求上樓梯的方案數。
題解:通過上一層轉移,沒啥坑。
代碼:
#include <iostream> #include <algorithm> #include <cmath> #include <cstring>
using namespace std; typedef long long ll; const int mod = 10007; int dp[15]; int main(void) { dp[1] = 1; dp[2] = dp[1] + 1; dp[3] = dp[2] + dp[1] + 1; dp[4] = dp[3] + dp[2] + dp[1] + 1; dp[5] = 0; dp[6] = dp[5] + dp[4] + dp[3] + dp[2]; dp[7] = 0; for (int i = 8; i <= 10; i++) dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3] + dp[i - 4]; cout << dp[10] << endl; return 0; }
答案:72
C:題目:求問在以下圖案的大三角形內部添加五條直線最多可以將大三角形分成多少個區域。
請在下圖的基礎上添加五條直線。
題解:
答案:47
D:題目:
有3030個籃子,每個籃子里有若干個蘋果,籃子里的蘋果數序列已經給出。
現在要把蘋果分給小朋友們,每個小朋友要么從一個籃子里拿三個蘋果,要么從相鄰的三個籃子里各拿一個蘋果。(蘋果可以剩余)
比如對於序列3 1 3,可以分給兩個小朋友變成0 1 0;也可以分給一個小朋友變成2 0 2,此時不能繼續再分了。所以答案是2。
求問對於以下序列{7,2,12,5,9,9,8,10,7,10,5,4,5,8,4,4,10,11,3,8,7,8,3,2,1,6,3,9,7,1}最多分給幾個小朋友?
題解:本題考慮貪心,貪心的策略是:從左到右取蘋果,使浪費的蘋果最少。籃子中大於等於3個蘋果的直接取3個,取到小於3個為止。然后將此籃子當作1 1 1取法的左端點,繼續取,直到無法操作。
這樣確保每次都是局部最優,因而推出整體最優。
代碼:
#include <iostream> #include <algorithm> #include <cstring>
using namespace std; typedef long long ll; //盡可能少剩下的蘋果最少為貪心思路
int main() { ios::sync_with_stdio(false); int sum = 0; int a[35] = { 0,7,2,12,5,9,9,8,10,7,10,5,4,5,8,4,4,10,11,3,8,7,8,3,2,1,6,3,9,7,1 }; int ans = 0; for (int i = 1; i <= 30; ++i) { ans += a[i] / 3; a[i] %= 3; /* for (int i = 1; i <= 30; i++) cout << a[i] << " "; cout << endl; */
while (a[i] >= 1 && a[i + 1] >= 1 && a[i + 2] >= 1) { a[i]--; a[i + 1]--; a[i + 2]--; ans++; } } cout << ans << endl; return 0; }
答案:62
E:題目:
廣場上的小朋友們排成了整齊的方陣。具體來說,我們可以把每個小朋友看做是一個點,那么小朋友們就形成了n×n的點陣。
方陣中,小朋友A和小朋友B互相可以看見,當且僅當二人之間的連線不經過別的小朋友,且他們之間的距離不超過k。我們想知道有多少對小朋友互相可以看見。(A,B)與 (B,A)算同一對。
例如,n=2,k=1時答案為4,n=2,k=2 時答案為6,n=3,k=2時答案為20。
現在我們想要知道,當n=1000,k=500時的答案是多少。由於答案過大,請回答對10^9+7取模后的結果。
分析:本題是填空題中唯一有難度的一問,坑點較多。(ps:用了對拍才找到自己的錯誤)
題解:
代碼:(正確代碼)
#include<bits/stdc++.h>
using namespace std; typedef long long ll; const int MOD = 1e9 + 7; int main() { int n, k; cin >> n >> k; ll ans = 2 * n * (n - 1) % MOD;//邊框上的線數目 for(int x = 1; x <= n; x++) { for(int y = 1; y <= n; y++) { if(x * x + y * y > k * k)continue; if(__gcd(x, y) != 1)continue; ans = (ans + 2 * (n - x) * (n - y)) % MOD; } } cout<<ans; return 0; }
解釋:對於代碼中ans的解釋
暴力對拍代碼:(直接跑可能要3000s才能出答案)
#include<bits/stdc++.h>
using namespace std; typedef long long ll; const int MOD = 1e9 + 7; int main() { int n, k, ans = 0; cin >> n >> k; ///第一個點(sx, sy) 第二個點(tx, ty)
for(int sx = 1; sx <= n; sx++) { for(int sy = 1; sy <= n; sy++) { for(int tx = 1; tx <= n; tx++) { for(int ty = 1; ty <= n; ty++) { if(sx == tx && sy == ty)continue; if((tx - sx) * (tx - sx) + (ty - sy) * (ty - sy) > k * k)continue; if(__gcd(abs(tx - sx), abs(ty - sy)) != 1)continue; ans++; } } } } cout<<ans / 2; return 0; }
答案:916585708
F:題目:
題解:直接用map或者set存即可,數據范圍不大,不會mle或者tle。
坑點:要把a0=1提前存入,不然就會只有13/15分。(別問為啥知道,問就是白給)
代碼:
#include <iostream> #include <algorithm> #include <cstdio> #include <cmath> #include <map>
using namespace std; typedef long long ll; const int mod = 10007; ll A, B, C; int a[2000005]; map<int, int>m; int main(void) { cin >> A >> B >> C; int flag = 0; a[0] = 1; m[1] = 1; for (int i = 1; i <= 2000000; i++) { a[i] = (A * a[i - 1] + a[i - 1] % B) % C; if (m[a[i]] == 0) m[a[i]]++; else { cout << i << endl; return 0; } } cout << "-1" << endl;
return 0; }
G:題目:
分析:一道稍微有點繁瑣,題面有錯的模擬。
輸出的第一行,應該是被破壞的道路,房屋,田地的總數。(十分的蛋疼,比賽的時候完全按照題面只有2/20分)
注意:
①濺射傷害是高級炮彈擊中的k*k區域的每一小塊周圍的8小塊都會受到的傷害。
②該開long long的地方不要吝嗇。
代碼:(按照修改后的題意)
#include <iostream> #include <algorithm> #include <cmath> #include <cstring> #include <map>
using namespace std; typedef long long ll; const int mod = 10007; const int maxn = 310; ll blood[4];//三種血量
ll sum[4];//三種受的傷
int mp[maxn][maxn];//編號地圖
ll now[maxn][maxn];//血量地圖
ll hurt[maxn][maxn];//基本傷害
int dx[8] = { 0,0,1,-1,1,1,-1,-1 }; int dy[8] = { 1,-1,0,0,1,-1,1,-1 }; int main(void) { ios::sync_with_stdio(false); int n, m; cin >> n >> m; cin >> blood[1] >> blood[2] >> blood[3]; int k; ll w;//范圍與濺射傷害
cin >> k >> w; for (int i = 1; i <= k; i++) for (int j = 1; j <= k; j++) cin >> hurt[i][j]; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { cin >> mp[i][j]; now[i][j] = blood[mp[i][j]]; } //計算攻擊
int q; cin >> q; while (q--) { int op, x, y; cin >> op >> x >> y;//爆炸中心x y
int s1, s2, e1, e2; s1 = max(1, x - k / 2);//行開始
e1 = min(n, x + k / 2);//行結束
s2 = max(1, y - k / 2);//列開始
e2 = min(m, y + k / 2);//列結束
for (int i = s1; i <= e1; i++) { for (int j = s2; j <= e2; j++) { int ii = (i - x) + (k + 1) / 2;//相對中心點的位置
int jj = (j - y) + (k + 1) / 2; if (now[i][j] > 0)//在受到炸彈之前還有血
{ sum[mp[i][j]] += min(now[i][j], hurt[ii][jj]); now[i][j] -= hurt[ii][jj]; if (now[i][j] < 0) now[i][j] = 0; } if (op == 0)//若有濺射傷害
{ for (int f = 0; f < 8; f++) { int i_new = i + dx[f]; int j_new = j + dy[f]; if (i_new<1 || i_new>n || j_new<1 || j_new>m) continue; if (now[i_new][j_new] > 0)//在受到濺射之前還有血
{ sum[mp[i_new][j_new]] += min(now[i_new][j_new], w); now[i_new][j_new] -= w; if (now[i_new][j_new] < 0) now[i_new][j_new] = 0; } } } } } } int ans[4] = { 0 }; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) ans[mp[i][j]] += (now[i][j] == 0); cout << ans[1] << " " << ans[2] << " " << ans[3] << endl; cout << sum[1] + sum[2] + sum[3] << endl; return 0; }
H:題目:
分析:如果按位展開,每次都暴力計算判斷是否乘除的話,必然拿不到滿分(強數據會tle)。
所以在計算是否整除的時候做了以下優化:
①第一次計算總和時,借助了已經打好的按位取模后的乘權表
②之后雙重for循環找交換的兩位字母,在取模環境下,減去兩個換前原字母所對應的值,加上兩個換后字母對應的值進行O(1)的更新判斷。
可行性分析:在無除法的運算過程中,加法和乘法的同余模定理確保了對每一步都取模后的結果不會讓最后的答案不變。
代碼:
#include <iostream> #include <algorithm> #include <cstdio> #include <cmath> #include <cstring>
using namespace std; typedef long long ll; ll f[2010];///f[i] = 26^i % M
int main(void) { string s; cin >> s; ll M; cin >> M; f[0] = 1 % M; int n = s.size(); for (int i = 1; i <= n; i++) f[i] = f[i - 1] * 26 % M; ll sum = 0; for (int i = 0; i < n; i++) sum = (sum + f[n - 1 - i] * (s[i] - 'A')) % M; if (sum == 0) { cout << "0 0" << endl; return 0; } else { for (int i = 0; i < s.size(); i++) { for (int j = i + 1; j < s.size(); j++) { ll tmp = sum; tmp = ((tmp - f[n - 1 - i] * (s[i] - 'A')) % M + M) % M; tmp = ((tmp - f[n - 1 - j] * (s[j] - 'A')) % M + M) % M; tmp = ((tmp + f[n - 1 - i] * (s[j] - 'A')) % M + M) % M; tmp = ((tmp + f[n - 1 - j] * (s[i] - 'A')) % M + M) % M; if (tmp == 0) { cout << i + 1 << " " << j + 1 << endl; return 0; } } } } cout << "-1 -1" << endl; return 0; }
注意:避免產生負數,取模之后的減法要加上模數,再取模確保計算結果為正。
I:題目:
分析:顯然,這一題的數據不能一個點跑一次dijkstra。我們發現對於起點1到所有點的最短路,可以一次dijkstra完成。但如果此時,選擇每一目的地出發,跑dijkstra,那需要再跑n-1次,顯然不是明智的選擇。
我們可以通過將邊反存,這樣所有從非起點指向1的邊變成了1指向所有目的地。只要再dijkstra一次反向存邊,便可計算出從目的地返回起點的最短路。
代碼:
#include <iostream> #include <algorithm> #include <cstdio> #include <cmath> #include <cstring> #include <map> #include <queue>
using namespace std; typedef long long ll; const int maxn = 60050; const ll INF = 1e15; struct edge { int v, w;///v表示終點 w表示邊長
edge() {} edge(int vv, int ww){v = vv;w = ww;} }; struct Heapnode { int id; ll d;///id表示節點的編號,d表示從s走到id的距離
Heapnode() {} Heapnode(int id, ll d) :id(id), d(d) {} bool operator <(const Heapnode& a)const { return d > a.d; } }; vector<edge>Map[2][maxn]; int n, m; ///添加一條從u出發到v結束,邊長為w的邊
void addedge(int u, int v, ll w) { Map[0][u].push_back(edge(v, w)); Map[1][v].push_back(edge(u, w));///反向建圖
} ll d[2][maxn];///d[i]表示從s出發,到達i的最短距離
bool vis[maxn]; void Dijkstra(int s, int id) { for (int i = 1; i <= n; i++)d[id][i] = INF, vis[i] = 0; d[id][s] = 0; priority_queue<Heapnode>q; q.push(Heapnode(s, 0)); while (!q.empty()) { Heapnode now = q.top(); q.pop(); int u = now.id; if (vis[u])continue; vis[u] = 1; for (int i = 0; i < Map[id][u].size(); i++) { ///u -> v 邊長為w
int v = Map[id][u][i].v; int w = Map[id][u][i].w; if (d[id][u] + w < d[id][v]) { d[id][v] = d[id][u] + w; q.push(Heapnode(v, d[id][v])); } } } } int main(void) { ios::sync_with_stdio(false); int T; cin >> T; while (T--) { cin >> n >> m; for (int i = 0; i <= n; i++) Map[0][i].clear(), Map[1][i].clear(); ll sum = 0; for (int i = 1; i <= m; i++) { int u, v; ll w; cin >> u >> v >> w; addedge(u, v, w); } Dijkstra(1, 0); Dijkstra(1, 1); for (int i = 2; i <= n; i++) sum += d[0][i] + d[1][i]; cout << sum << endl; } return 0; }
注意:不確定數值大小的話,都用long long(除了惡心的多校之外,一般不會卡ll);初始化的INF一定要大一些,如果使用0x3f3f3f3f會出錯
J:題目:
分析:這是一題很棒的搜索題,題目中說確保每個點是至多是一個傳送門的入口(僅僅只能說明傳送門的出度是唯一的,即一個傳送門對應一個目的地)
例如下圖,
因此說明,傳送門的傳送位置是傳送門;傳送門死循環等狀況也是可能的。
所以對於任意一點非障礙物a有以下情況:
(一)a是傳送門
1:經過終點
2:傳n次,落在合法位置
3:傳n次,卡在障礙物
4:死循環
(二)a不是傳送門
所以如果利用隊列,我們需要將下一個即將壓入隊列的點進行預處理,才能當作正常bfs搜索操作。
難點處理:我們利用了map去映射傳送門的起點對和終點對,寫了函數Find_Next(a)去尋找點a進入傳送門后的結果
1:正常情況:返回出口
2:卡在障礙物:返回-1,-1
3:死循環:返回-1,-1
4:只要經過終點:返回終點
代碼:
#include <iostream> #include <algorithm> #include <cstdio> #include <cmath> #include <cstring> #include <map> #include <queue> #include <set>
using namespace std; typedef long long ll;const int N = 1010; const int inf = 0x3f3f3f3f; char Mp[N][N];//地圖
bool vis[N][N];//標記
int dx[4] = { 1,-1,0,0 }; int dy[4] = { 0,0,1,-1 }; int x, y;//目的地
int n, m; typedef pair<int, int> Pair; map<Pair, Pair>door; struct node { int x, y, num; node() {} node(int xx, int yy, int nnum) { x = xx; y = yy; num = nnum; } node(Pair a, int nnum) { x = a.first; y = a.second; num = nnum; } }; ///a是傳送門入口 返回傳送門出口 ///1:正常情況:返回出口 ///2:卡在障礙物:返回-1,-1 ///3:死循環:返回-1,-1 ///4:只要經過終點:返回終點
Pair Find_Next(Pair a) { Pair start = a; set<Pair>s;///走過的點全都塞入s中
while (true) { if (a.first == x && a.second == y)return make_pair(x, y); if (Mp[a.first][a.second] == '*')return make_pair(-1, -1); if (door.count(a)) { if (s.count(a))return make_pair(-1, -1); s.insert(a); a = door[a]; } else return a; } } int main() { ios::sync_with_stdio(false); cin >> n >> m; for (int i = 1; i <= n; i++) cin >> (Mp[i] + 1); int k; cin >> k; while (k--) { int a, b, c, d; cin >> a >> b >> c >> d; door[make_pair(a, b)] = make_pair(c, d); } cin >> x >> y; Pair Start = make_pair(1, 1); if (Find_Next(Start).first == -1) { cout << "No solution" << endl; return 0; } queue<node>q; Start = Find_Next(Start); q.push(node(Start, 0)); vis[Start.first][Start.second] = 1; while (!q.empty()) { node now = q.front(); q.pop(); if (now.x == x && now.y == y) { cout << now.num << endl; return 0; } for (int i = 0; i < 4; i++) { int xx = now.x + dx[i]; int yy = now.y + dy[i]; if (xx < 1 || xx > n || yy < 1 || yy > m)continue; if (Mp[xx][yy] == '*')continue; if (vis[xx][yy])continue; Pair Next = Find_Next(make_pair(xx, yy)); //cout<<xx<<" "<<yy<<" "<<Next.first<<" "<<Next.second<<endl;
if (Next.first == -1)continue; q.push(node(Next, now.num + 1)); vis[Next.first][Next.second] = 1; } } cout << "No solution" << endl; return 0; }
Ps:題目數據似乎不強,沒有涉及到傳送門死循環的數據,但是處於嚴謹還是考慮上啦(畢竟補題)