序言:
寒假就要結束了,馬上就要上網課了,剩下的時間就要複習和預習大一下的知識了,博客可能暫時停更了,謝謝大家的觀看。武漢加油!中國加油!
今天是寒假集訓的最後一次考試了,題很不錯,思維題較多,涉及算法的並不多,感謝jwGG以及實驗室的學長學姐們親情出題,謝謝cy老師和jwGG的假期培訓。
北大的dalao屠榜,😂,差距很大呀,我們仍需努力。
題解:
A:熊熊對對碰
map計數器的應用,因爲他要計算相反數和他本身的和,所以0的時候要考慮。
AC代碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
int n;
map<pair<int, int>, int> a;
pair<int, int> q;
int main()
{
scanf("%d", &n);
while (n--)
{
int x, y;
scanf("%d%d", &x, &y);
if (a.count({-1 * x, -1 * y}) != 0) //如果它的相反數存在,就直接計算進去
{
a[{-1 * x, -1 * y}]++;
}
else //若他的相反是不存在,就計算它本身
a[{x, y}]++;
}
int cnt = 0;
for (map<pair<int, int>, int>::iterator i = a.begin(); i != a.end(); ++i)
{
if ((*i).second % 2 == 0)
cnt += (*i).second;
}
printf("%d\n", cnt);
return 0;
}
B:祕籍
前綴和+快慢指針尺取法的應用,暴力也可以。
AC代碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const int maxn = 300002;
int n, k;
int a[maxn];
bool flag = 0;
int main()
{
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++)
{
int x;
scanf("%d", &x);
a[i] = a[i - 1] + x; //前綴和
}
int j = 1;
for (int i = 0; i <= n && j <= n;) //快慢指針
{
if (a[j] - a[i] < k)
j++;
else if (a[j] - a[i] > k)
i++;
else
{
printf("%d %d", i + 1, j);
flag = 1;
break;
}
}
if (flag == 0)
puts("tiangeniupi");
return 0;
}
C:jwGG的簽到題
數學公式的推導+審題,一開始沒審清題WA了兩發。因爲數據範圍的情況,要開unsigned long long。
AC代碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
int main()
{
int t;
ll a[20] = {0, 9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999, 9999999999, 99999999999, 999999999999, 9999999999999, 99999999999999, 999999999999999, 9999999999999999, 99999999999999999, 999999999999999999};
scanf("%d", &t);
while (t--)
{
unsigned long long cnt = 0;
unsigned long long x, y;
scanf("%llu%llu", &x, &y);
for (int i = 1; i <= 18; i++)
{
if (a[i] <= y)
++cnt;
if (a[i] > y)
break;
}
cnt *= x;
printf("%llu\n", cnt);
}
return 0;
}
D:jwMM的瘋狂A-B
最簡單的簽到題,set的應用。
AC代碼:
#include <bits/stdc++.h>
using namespace std;
int n, m;
int x;
set<int> a, b;
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
{
scanf("%d", &x);
a.insert(x);
}
for (int i = 1; i <= m; i++)
{
scanf("%d", &x);
b.insert(x);
}
int cnt = 0, flag = 0;
for (set<int>::iterator it = a.begin(); it != a.end(); it++)
{
if (!b.count(*it))
{
printf("%d\n", *it);
cnt++;
flag = 1;
}
}
if (!flag)
puts("So crazy!!!");
return 0;
}
E:煊哥的難題
太坑了。一開始我想了三種方法:第一種:叉積+直線重合,可以避免計算時的精度問題,但是比較難優化,只能暴力。第二種:斜率+截距的方法,可以進行map計數優化,但是計算時具有精度問題。第三種,還是斜率+截距,但是斜率用最簡的式表示,不用直接相除。
但最後,各種權衡之下,我還是選擇了第一種,然後就TLE,哎。
晚上看完jwGG講解後,標程就是選擇的第二種方法,那我就來說說第二組方法的題解吧。
-
選擇map進行計數 :在斜率存在時分別記錄斜率、y軸截距;在斜率不存在時,也就是垂直的時候,記錄垂直的條數和x軸截距。
-
在輸入座標的時候進行更新答案。
-
直線與直線的關係有三種:第一種:重合;第二種:相交;第三種:平行。雖然直線重合,但是這個也計算相交。我們只需要把當前錄入的所有直線 - 與當前直線平行的直線的數量。平行的直線=相同的斜率數量 - 相同的截距數量。
AC代碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const int maxn = 100002;
int n, x[3], y[3];
double td, tk;
map<double, int> k; //斜率->數量
map<pair<double, double>, int> d; //斜率,截距->數量,重合
map<int, int> d2; //垂直時候,截距->數量,重合
int cnt = 0; //垂直總個數
ll ans = 0;
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= 2; j++)
scanf("%d%d", &x[j], &y[j]);
if (x[1] == x[2]) //斜率不存在情況
{
td = x[1]; //x截距
d2[td]++;
cnt++;
ans += (i - 1 - (cnt - d2[td]));
}
else
{
tk = (y[2] - y[1]) * 1.0 / (x[2] - x[1]); //k
td = y[1] - tk * x[1]; //y截距
k[tk]++;
d[{tk, td}]++;
ans += (i - 1 - (k[tk] - d[{tk, td}]));
}
}
printf("%lld\n", ans);
return 0;
}
F: jwGG與yzMM的字符串
字符串的模擬題,先打解碼錶,最難想到的時解碼錶的打法,還有反推的過程。
- 我們選擇利用二維數組來進行解碼:密匙爲x,原文是y,加密後的文件爲z;這個式子代表用密匙x 來對加密後的文件z進行解密,還原成原文z。
- 我們如何得到d數組呢?這時候就要預先打標。
- 加密是正向加密,解密就要反向解密。
AC代碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const int maxn = 100002;
pair<int, int> p[maxn];
char a[55], b[130][130];
string s[1005];
void init()
{
for (int i = 0; i <= 51; i++)
{
if (i <= 25)
a[i] = 'A' + i;
else
a[i] = 'a' + i - 26;
}
for (int i = 0; i <= 51; i++) //x
for (int j = 0; j <= 51; j++) //y
{
int z = (i + j) % 52; //替換後的z,b中是存字符對應的ASCLLII
b[a[i]][a[z]] = a[j];
}
}
int main()
{
ios::sync_with_stdio(false);
int n, m;
init();
cin >> n >> m;
for (int i = 1; i <= m; i++)
cin >> p[i].first >> p[i].second;
for (int i = 1; i <= n; i++)
cin >> s[i];
for (int i = m; i >= 1; i--)
{
int y = p[i].second;
int x = p[i].first;
int len1 = s[x].size(), len2 = s[y].size();
for (int i = 0; i < len2; i++)
s[y][i] = (b[s[x][i % len1]][s[y][i]]);
}
for (int i = 1; i <= n; i++)
cout << s[i] << endl;
return 0;
}
G:jwGG與bwMM的字符串
暫時未作出,後續更新…
H:庫特放書
一道披着二分的暴力查找答案的題,直接放出題人的題解。
爲什麼直接暴力不用二分在時間上是可行的吧,因爲我看所有通過代碼都寫的枚舉容量的上界是sum,但是如果上界真的是sum的話在時間上理論上是不可行的,這一點上並不是我數據出水了,是它的下界可以視爲max(ceil(sum / k),maxV),上界爲ceil(sum / k)+MAXV 所以枚舉次數爲二者之差遠達不到sum次,最多也不會超過1000次所以從下界while(1)向上尋找在時間上是可行的。
AC代碼:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <bitset>
#include <cstdlib>
#include <cmath>
#include <set>
#include <list>
#include <deque>
#include <map>
#include <queue>
#include <iomanip>
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
int t, n, m;
int v[1002];
bool vis[1002];
bool check(int tt)
{
memset(vis, 0, sizeof(vis));
for (int i = 1; i <= m; i++)
{
int sum = 0;
for (int j = 1; j <= n; j++)
{
if (sum + v[j] <= tt && vis[j] == 0)
{
sum += v[j];
vis[j] = 1;
}
}
}
for (int i = 1; i <= n; i++)
{
if (vis[i] == 0) //no
return 0;
}
return 1;
}
int main()
{
scanf("%d", &t);
int k = 0;
while (t--)
{
int sum = 0;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
{
scanf("%d", &v[i]);
sum += v[i];
}
sort(v + 1, v + 1 + n, greater<int>());
int l = sum / m, r = sum, ans;
for (int i = l; i <= r; i++)
{
if (check(i))
{
ans = i;
break;
}
}
printf("Case #%d: %d\n", ++k, ans);
}
return 0;
}