文章目录
- 牛客小白月赛103(打表、二进制、几何、思维)
- A. 小冰的正多边形
- B. 冰冰的电子邮箱
- C. 冰冰的异或(打表、二进制)
- D. 冰冰的分界线(几何、浮点数处理)
- E. 冰冰的 GCD(调和级数、思维)
据说F是假题,就没看了。
牛客小白月赛103(打表、二进制、几何、思维)
A. 小冰的正多边形
根据题意,判断是否可以构成正三角形。如可以,答案取周长最小的正三角形的周长。
#include<bits/stdc++.h>
using namespace std;
int main(){
int ncase;
cin >> ncase;
while(ncase--){
int n;
cin >> n;
map<int, int> m;
int res = 1e9, x;
for(int i = 1; i <= n; i++){
cin >> x;
m[x]++;
if(m[x] == 3) res = min(res, 3 * x);
}
if(res == 1e9) cout << "no" << endl;
else{
cout << "yes" << endl;
cout << res << endl;
}
}
return 0;
}
B. 冰冰的电子邮箱
字符串处理,根据题意判断即可。
#include<bits/stdc++.h>
using namespace std;
int check_char(char c){
if(c >= 'a' && c <= 'z') return 1;
if(c >= 'A' && c <= 'Z') return 1;
if(c >= '0' && c <= '9') return 1;
return 0;
}
int main(){
int ncase;
cin >> ncase;
while(ncase--){
string s;
cin >> s;
string a, b;
int flag = 0;
for(auto c : s){
if(c == '@' && flag == 0) flag = 1;
else if(flag == 0) a += c;
else b += c;
}
int res = 1;
if(a.size() == 0 || a.size() > 64) res = 0;
for(int i = 0; i < a.size(); i++){
if(check_char(a[i]) == 0){
if(a[i] == '.'){
if(i == 0 || i+1 == a.size()) res = 0;
else continue;
}
else res = 0;
}
}
if(b.size() == 0 || b.size() > 255) res = 0;
for(int i = 0; i < b.size(); i++){
if(check_char(b[i]) == 0){
if(b[i] == '.' || b[i] == '-'){
if(i == 0 || i+1 == b.size()) res = 0;
else continue;
}
else res = 0;
}
}
cout << (res ? "Yes" : "No") << endl;
}
return 0;
}
C. 冰冰的异或(打表、二进制)
观察输入 n 的范围,单次查询需要在O(1) 或 O(log(n)) 的时间内,打表观察规律, r e s = 2 ⌈ l o g ( n ) ⌉ , n > 2 res = 2^{\lceil {log(n)} \rceil}, n > 2 res=2⌈log(n)⌉,n>2。
PS:这里的打表,即暴力枚举对于每一个 n,观察 n 与其对应的答案的规律。
对于规律的证明:
对于 4 为二进制,取X = 1000,设 Y = 0ABC, ABC为任意的01,此时,可以得到 [ 100 0 2 , 111 1 2 ] [1000_2, 1111_2] [10002,11112] 之间的所有数。
取X = 0100,设 Y = 00BC, BC为任意的01,此时,可以得到 [ 010 0 2 , 011 1 2 ] [0100_2, 0111_2] [01002,01112] 之间的所有数。
取X = 0010,设 Y = 000C, C为任意的01,此时,可以得到 [ 001 0 2 , 001 1 2 ] [0010_2, 0011_2] [00102,00112] 之间的所有数。
取X = 0001,设 Y = 0000, C为任意的01,此时,可以得到 [ 000 1 2 , 000 1 2 ] [0001_2, 0001_2] [00012,00012] 之间的所有数。
根据题意,Y ≠ 0,故而,上述四种情况中,需要排除左端点 100 0 2 、 010 0 2 、 001 0 2 、 000 1 2 1000_2、0100_2、0010_2、0001_2 10002、01002、00102、00012 。即,除这四个数外,其他数都可以被覆盖。
考虑覆盖这四个数:
即, 001 1 2 ⨁ 001 0 2 = 000 1 2 , 001 1 2 ⨁ 000 1 2 = 001 0 2 , 010 1 2 ⨁ 000 1 2 = 010 0 2 0011_2 \bigoplus 0010_2 = 0001_2,0011_2 \bigoplus 0001_2 = 0010_2,0101_2 \bigoplus 0001_2 = 0100_2 00112⨁00102=00012,00112⨁00012=00102,01012⨁00012=01002。
由于 001 1 2 ⨁ 001 0 2 = 000 1 2 0011_2 \bigoplus 0010_2 = 0001_2 00112⨁00102=00012,当 n 大于 2 时,才能构造出 1。
同时,可以发现, 100 0 2 1000_2 10002 是无法用 1 ~ n 的数进行异或得到。
综上,当 n > 2时,对于 n:
- n > 2 时, 2 ⌈ l o g ( n ) ⌉ 2^{\lceil {log(n)} \rceil} 2⌈log(n)⌉ 是无法被表示的为 2 ⌈ l o g ( n ) ⌉ 2^{\lceil {log(n)} \rceil} 2⌈log(n)⌉ ,即 n 在二进制表示时的最高位
- n <= 2 时,1 无法被表示
#include<bits/stdc++.h>
using namespace std;
int main(){
int ncase;
cin >> ncase;
while(ncase--){
long long n;
cin >> n;
long long res = 0, x = n;
while(x) res++, x /= 2;
res -= (pow(2, res-1) == n);
if(n <= 2) cout << 1 << endl;
else cout << (long long)(pow(2, res)) << endl;
}
return 0;
}
D. 冰冰的分界线(几何、浮点数处理)
枚举所有点对,对能构成的直线去重即可。(”点对“:题目给出n个点中,任意两个不同的点)
显然难点在于:如何表示直线?浮点数如何处理?(浮点数存储原理,不懂的自行百度)
题目所求的直线,就是每个点对所连成的线段的垂线所在的直线。
如下图所示,y1就是点对AB所满足的直线。(y1 垂直于 AB)
两点确定一条直线,但是对于直线y1,已知只有一个点C。
考虑更多的信息,y1是线段AB的垂线,即 k A B ∗ k y 1 = − 1 k_{AB} * k_{y1} = -1 kAB∗ky1=−1,求出线段AB所在直线的斜率 k 即可得到 y1的斜率 k y 1 k_{y1} ky1。
有点 C 的坐标和斜率斜率 k y 1 k_{y1} ky1,即可确认 y1。
对于斜率k,肯定是不能使用浮点数存储的,考虑使用方向向量替代斜率。
k = B / A , B = ( y 1 − y 2 ) , A = ( x 1 − x 2 ) k = B / A, B = (y1 - y2), A = (x1 - x2) k=B/A,B=(y1−y2),A=(x1−x2),其中 ( A , B ) (A,B) (A,B) 即为直线的方向向量。
对于点对AB,求出其方向向量 ( A , B ) (A, B) (A,B),根据 k A B ∗ k y 1 = − 1 k_{AB} * k_{y1} = -1 kAB∗ky1=−1,得到 y1 的方向向量。
根据 y = k x + b y = kx + b y=kx+b,求出 b,即可确定唯一的直线。
对于 y = k x + b y = kx + b y=kx+b,带入 k = B / A k = B / A k=B/A ,得到 y = B / A ∗ x + b y = B / A * x + b y=B/A∗x+b。
显然,直接表示 b b b 容易出现小数。为了避免小数对等式两边同乘A,得到 A y = B ∗ x + A b Ay = B * x + Ab Ay=B∗x+Ab。
使用 A b Ab Ab 与直接使用 b b b 有相同的效果。
对于相同的 ( A , B ) (A,B) (A,B),记录不同的 A b Ab Ab 的数量,求和即可。
最后一点细节,向量 ( A , B ) (A,B) (A,B)而言:
比如 ( 1 , 2 ) 、 ( 2 , 4 ) 、 ( − 3 , − 6 ) (1,2)、(2,4)、(-3,-6) (1,2)、(2,4)、(−3,−6),他们三个表示的是同一个向量,需要统一。
令 A A A 大于零, A A A 和 B B B 都除 g c d ( a b s ( A ) , a b s ( B ) ) gcd(abs(A), abs(B)) gcd(abs(A),abs(B)) 即可。
同时,注意A无穷大的情况。(k不存在,x1 = x2)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 5;
int x[maxn], y[maxn];
map<pair<int, int>, int> ID;
set<int> s[maxn * maxn];
int ids = 0;
int get_id(pair<int, int> p){
if(ID[p] == 0) return ID[p] = ++ids;
else return ID[p];
}
int gcd(int a, int b){
if(b == 0) return a;
else return gcd(b, a % b);
}
void init(){
for(int i = 0; i <= ids; i++) s[i].clear();
ids = 0; ID.clear();
}
int main(){
int ncase;
cin >> ncase;
while(ncase--){
int n;
cin >> n;
for(int i = 1; i <= n; i++) cin >> x[i];
for(int i = 1; i <= n; i++) cin >> y[i];
for(int i = 1; i <= n; i++){
for(int j = i+1; j <= n; j++){
int A = -1 * (y[i] - y[j]), B = x[i] - x[j]; // 原直线的法向量,即其垂线的方向向量
if(A == 0) s[0].insert(x[j] + x[i]); // 一种特殊情况. k 无穷大
else{
if(A < 0) A *= -1, B *= -1;
int g = gcd(abs(A), abs(B));
A = A / g, B = B / g; // 计算最简的方向向量
pair<int, int> p = {A, B};
int b = A * (y[i]+y[j]) - B * (x[i]+x[j]); // 计算 2Ab
s[get_id(p)].insert(b);
}
}
}
int res = 0;
for(int i = 0; i <= ids; i++) res += s[i].size();
cout << res << endl;
init();
}
return 0;
}
E. 冰冰的 GCD(调和级数、思维)
根据题意,预处理出每个 f ( x ) f(x) f(x) 和 g ( x ) g(x) g(x) 的值,O(1) 查询即可。
对于 f ( x ) f(x) f(x)的预处理:
逆序遍历排列 b b b,对于每个 b i b_i bi,设 p p p 表示满足 g c d ( b i , b p ) ≠ 1 gcd(b_i, b_p) ≠ 1 gcd(bi,bp)=1 的第一个位置,初始为 n − 1 n-1 n−1。
枚举 b i b_i bi 的所有因子 x x x。如因子 x x x,在之前的枚举出现过,令 p = m i n ( p , v [ x ] ) p = min(p, v[x]) p=min(p,v[x])。随后,更新 v [ x ] v[x] v[x],令 v [ x ] = i v[x] = i v[x]=i。
枚举完所有因子后,
- 如 p = n − 1 p = n-1 p=n−1,则 f ( i ) = b i f(i) = b_i f(i)=bi
- 否则, f ( i ) f(i) f(i) = g c d ( b i , b p ) gcd(b_i, b_p) gcd(bi,bp)
时间复杂度: n ∗ ( n ) n*\sqrt(n) n∗(n)
对于 g ( x ) g(x) g(x) 的预处理:
顺序遍历排列 c,对于每个 c i c_i ci,枚举 i 的倍数,判断其倍数的位置在排列 a 中是否大于 c_i 即可。
时间复杂度: n ∗ l o g ( n ) n * log(n) n∗log(n) ,调和级数,自行百度。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
int a[maxn], b[maxn], c[maxn];
int pa[maxn], pb[maxn];
int f[maxn], g[maxn], v[maxn];
int gcd(int a, int b){
if(b == 0) return a;
else return gcd(b, a % b);
}
int main(){
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 1; i <= n; i++) cin >> b[i];
for(int i = 1; i <= n; i++) cin >> c[i];
for(int i = 1; i <= n; i++) pb[b[i]] = i;
for(int i = 1; i <= n; i++) pa[a[i]] = i;
for(int i = n; i >= 1; i--){
int x = b[i], p = n+1;
for(int j = 1; j * j <= x; j++){ // 枚举 x 的因子
if(x % j == 0){
if(j != 1 && v[j] > i) p = min(p, v[j]);
if(v[x/j] > i) p = min(p, v[x/j]);
v[j] = v[x/j] = i;
}
}
f[i] = (p == n+1 ? b[i] : gcd(b[i], b[p]));
}
for(int i = 1; i <= n; i++){ // nlog(n)
for(int j = i; j <= n; j += i){
if(pa[j] >= c[i]){
g[i]++;
}
}
}
while(m--){
int x;
cin >> x;
cout << g[f[x]] << endl;
}
return 0;
}