2024牛客寒假算法基础集训营6
A
void solve()
{
vector<int> pr;
vector<bool> not_pr(N);
auto getpr = [&](int n)
{
for (int i = 2; i <= n; ++i)
{
if (!not_pr[i])
pr.push_back(i);
for (int p : pr)
{
if (i * p > n)
break;
not_pr[i * p] = true;
if (i % p == 0) // 说明i*p已经被一个更小的i判断过了
break;
}
}
};
getpr(100);
ll l, r;
cin >> l >> r;
for (int i = 0; i < pr.size(); i++)
for (int j = i + 1; j < pr.size(); j++)
for (int k = j + 1; k < pr.size(); k++)
{
ll res = pr[i] * pr[j] * pr[k];
if (res >= l && res <= r)
{
cout << res << endl;
return;
}
}
cout << "-1" << endl;
}
B
对b数组的每个数找最接近的a
void solve()
{
int n;
cin >> n;
vector<ll> a(n, 0), b(n, 0), c(n, 0);
for (int i = 0; i < n; i++)
cin >> a[i];
for (int i = 0; i < n; i++)
cin >> b[i];
c = a;
sort(a.begin(), a.end());
int p1 = 0, p2 = 0, mi = 1e9;
for (int i = 0; i < n; i++)
{
int pos = lower_bound(a.begin(), a.end(), b[i]) - a.begin();
if (pos < n)
if (abs(b[i] - a[pos]) < mi)
mi = abs(b[i] - a[pos]), p1 = i, p2 = a[pos];
if (pos - 1 >= 0)
if (abs(b[i] - a[pos - 1]) < mi)
mi = abs(b[i] - a[pos - 1]), p1 = i, p2 = a[pos - 1];
}
for (int i = 0; i < n; i++)
if (c[i] == p2)
{
p2 = i;
break;
}
swap(c[p1], c[p2]);
for (auto i : c)
cout << i << " ";
cout << endl;
}
C
类似二进制,从大到小组合即可
证明类似二进制背包 显然
#include <bits/stdc++.h>
#define endl '\n'
#define x first
#define y second
#define ls(x) (a[x].l)
#define rs(x) (a[x].r)
#define sum(x) a[x].sum
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef double db;
const ll mod = 1e9 + 7;
const int N = 1e6 + 100, M = 25;
random_device rd;
mt19937_64 gen(rd());
ll f[50];
void solve()
{
int n;
cin >> n;
vector<int> ans;
for (int i = 45; i >= 0; i--)
{
while (n >= f[i] && ans.size() < 3)
n -= f[i], ans.push_back(f[i]);
}
if (!n)
{
for (auto i : ans)
cout << i << " ";
cout << endl;
}
else
cout << "-1" << endl;
}
int main()
{
f[1] = 1, f[2] = 1;
for (int i = 2; i <= 45; i++)
f[i] = f[i - 1] + f[i - 2];
// cout << setprecision(5);
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int _ = 1;
cin >> _;
while (_--)
solve();
return 0;
}
D
两种情况,自己让二追三,对面让二追三
void solve()
{
db p;
cin >> p;
db res = (1 - p) * (1 - p) * p * p * p;
res += p * p * (1 - p) * (1 - p) * (1 - p);
cout << res << endl;
}
E
模拟
void solve()
{
string s1, s2;
cin >> s1 >> s2;
int x = 0;
for (auto i : s1)
if (i <= '9' && i >= '0')
x = x * 10 + i - '0';
x=x/2+1;
int k = 0, y = 0;
for (auto i : s2)
{
if (i == 'R')
k++;
else
y++;
if (k == x)
{
cout << "kou!" << endl;
cout << k + y << endl;
return;
}
if (y == x)
{
cout << "yukari!" << endl;
cout << k + y << endl;
return;
}
}
cout << "to be continued." << endl;
cout << s2.size() << endl;
}
F
有相同质因子的数一定是要放在一起,感觉难点在于怎样实现
预处理\(10^3\)以内的所有质数(大于\(\sqrt n\)的质因数只有一个)
对每个数分解质因数,然后bfs每个质因数能拓展到哪些数
#include <bits/stdc++.h>
#define endl '\n'
#define x first
#define y second
#define ls(x) (a[x].l)
#define rs(x) (a[x].r)
#define sum(x) a[x].sum
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef double db;
const ll mod = 1e9 + 7;
const int N = 1e6 + 100, M = 25;
random_device rd;
mt19937_64 gen(rd());
vector<int> pr;
vector<bool> not_pr(N, 0);
void getpr(int n)
{
for (int i = 2; i <= n; ++i)
{
if (!not_pr[i])
pr.push_back(i);
for (int p : pr)
{
if (i * p > n)
break;
not_pr[i * p] = true;
if (i % p == 0) // 说明i*p已经被一个更小的i判断过了
break;
}
}
}
void solve()
{
auto cal = [&](int x) -> vector<int>
{
vector<int> res;
for (auto p : pr)
if (x % p == 0)
{
res.push_back(p);
while (x % p == 0)
x /= p;
}
if (x > 1)
res.push_back(x);
return res;
};
int n, t;
cin >> n;
vector<ll> ans1, ans2;
vector<vector<int>> fac;
vector<int> a;
vector<bool> bel(n, 0);
for (int i = 1; i <= n; i++)
{
int x;
cin >> x;
a.push_back(x);
fac.push_back(cal(x));
}
set<int> st;
bel[0] = 1, t = 1;
for (auto x : fac[0])
st.insert(x);
while (t)
{
t = 0;
for (int i = 0; i < n; i++)
if (!bel[i])
{
for (auto x : fac[i])
if (st.count(x))
{
bel[i] = 1, t = 1;
for (auto e : fac[i])
st.insert(e);
break;
}
}
}
for (int i = 0; i < n; i++)
if (bel[i])
ans1.push_back(a[i]);
else
ans2.push_back(a[i]);
if (ans1.empty() || ans2.empty())
{
cout << "-1 -1" << endl;
return;
}
cout << ans1.size() << " " << ans2.size() << endl;
for (auto i : ans1)
cout << i << " ";
cout << endl;
for (auto i : ans2)
cout << i << " ";
cout << endl;
}
int main()
{
getpr(1e3);
cout << setprecision(10);
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int _ = 1;
cin >> _;
while (_--)
solve();
return 0;
}
G
构造类似2,1,2,1,2,1,2
最少需要2k+1个空,数组和至少需要n+k+1(2,1,2,1,1,1)
如果k为0要特判,否则如果s=n,会有n+0+1>s,但实际上只需要n(1,1,1,1)
接下来考虑怎样构造
先构造k个三元组,奇数位取尽可能大,偶数位取1
如果2k+1<n,直接全部放到后面
如果2k+1=n,均匀的放在偶数位
void solve()
{
int n, s, k;
cin >> n >> s >> k;
if (2 * k + 1 > n)
{
cout << "-1" << endl;
return;
}
if (k && n + k + 1 > s)
{
cout << "-1" << endl;
return;
}
vector<int> ans(n + 1, 0);
int add = s - (n - (k + 1));
for (int i = 1; i <= k + 1; i++)
ans[2 * i - 1] = add / (k + 1);
for (int i = 1; i <= k; i++)
ans[2 * i] = 1;
s -= ((add / (k + 1)) * (k + 1)) + k;
if (s)
{
if (n > 2 * k + 1)
{
int num = n - (2 * k + 1);
for (int i = 2 * k + 2, mo = s % num; i <= n; i++, mo--)
ans[i] = s / num + (mo > 0);
}
else
{
for (int i = 1, mo = s % k; i <= k; i++, mo--)
{
ans[2 * i] += s / k + (mo > 0);
if (ans[2 * i] >= ans[2 * i - 1])
{
cout << "-1" << endl;
return;
}
}
}
}
for (int i = 1; i <= n; i++)
cout << ans[i] << " ";
cout << endl;
}
H
首先要会求直线与圆的交点个数
设直线的与圆的交点数量为x
那么这条直线对答案的贡献是\(x*\frac{n-2}{C(n,3)}\)
但是有种特别情况,点恰好在圆上,这样他会多贡献\(C(n,m)\),需要减去
struct P
{
ll x, y;
P() {}
P(ll _x, ll _y) : x(_x), y(_y) {}
};
struct C
{
ll r, x, y;
C() {}
C(ll r, ll x, ll y) : r(r), x(x), y(y) {}
};
int cl(P p1,P p2,C c)
{
ll res1=(p1.x-c.x)*(p1.x-c.x)+(p1.y-c.y)*(p1.y-c.y);
ll res2=(p2.x-c.x)*(p2.x-c.x)+(p2.y-c.y)*(p2.y-c.y);
bool flag1=res1<c.r*c.r;
bool flag2=res2<c.r*c.r;
if(flag1&&flag2) //情况一、两点都在圆内 :一定不相交
return 0;
else if(flag1||flag2) //情况二、一个点在圆内,一个点在圆外:一定相交
return 1;
else //情况三、两个点都在圆外
{
__int128_t A,B,C,dist1,dist2,angle1,angle2;
//将直线p1p2化为一般式:Ax+By+C=0的形式。先化为两点式,然后由两点式得出一般式
A=p1.y-p2.y;
B=p2.x-p1.x;
C=p1.x*p2.y-p2.x*p1.y;
//使用距离公式判断圆心到直线ax+by+c=0的距离是否大于半径
dist1=A*c.x+B*c.y+C;
dist1*=dist1;
dist2=(A*A+B*B)*c.r*c.r;
if(dist1>dist2)//圆心到直线p1p2的距离大于半径,不相交
{
return 0;
}
//角cp1p2和cp2p1余弦正负性,余弦定理一通化简
angle1=(c.x-p1.x)*(p2.x-p1.x)+(c.y-p1.y)*(p2.y-p1.y);
angle2=(c.x-p2.x)*(p1.x-p2.x)+(c.y-p2.y)*(p1.y-p2.y);
if(angle1>0&&angle2>0)//余弦为正,则是锐角,一定相交
{
if(dist1==dist2) return 1;
else return 2;
}
else
{
if(res1==c.r*c.r||res2==c.r*c.r) return 1;
else return 0;
}
}
}
void solve()
{
C c;
cin >> c.x >> c.y >> c.r;
ll n, cnt = 0;
cin >> n;
vector<P> p(n);
for (auto &[x, y] : p)
{
cin >> x >> y;
ll dx=c.x-x,dy=c.y-y;
if (dx * dx + dy * dy == c.r * c.r)
cnt++;
}
db ans = 0;
for (int i = 0; i < n; i++)
for (int j = i+1; j < n; j++)
ans += cl(p[i], p[j], c);
ans = ans * 6. / (n * (n - 1));
ans -= cnt * 3. / n;//num*C(n,2)*((n-2)/C(n,3))
cout << ans << endl;
}
I
答案只有几种情况
最大字段和mx,最小字段和mi
mx(+)*mx(+)
mi(-)*mi(-)
mx(-)*mi(+)
求出a,b的最大最小字段和,然后暴力枚举四种情况求max
ll t1[2], t2[2];
void solve()
{
ll n, m;
cin >> n >> m;
vector<ll> a(n + 1, 0), b(m + 1, 0);
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= m; i++)
cin >> b[i];
auto dpmx = [&](vector<ll> &s, int n) -> ll
{
vector<ll> f(n + 1, 0);
for (int i = 1; i <= n; i++)
f[i] = max(s[i], f[i - 1] + s[i]);
return *max_element(f.begin() + 1, f.end());
};
auto dpmi = [&](vector<ll> &s, int n) -> ll
{
vector<ll> f(n + 1, 0);
for (int i = 1; i <= n; i++)
f[i] = min(s[i], f[i - 1] + s[i]);
return *min_element(f.begin() + 1, f.end());
};
t1[0] = dpmi(a, n);
t1[1] = dpmx(a, n);
t2[0] = dpmi(b, m);
t2[1] = dpmx(b, m);
ll ans = -1e18;
for (int i = 0; i <= 1; i++)
for (int j = 0; j <= 1; j++)
ans = max(ans, t1[i] * t2[j]);
cout << ans << endl;
}
J
dp做法:
记\(f_{u,i}\)为以u为根的子树,权值和%3为i,的方案(选的是1/2)
暴力转移即可
如何求方案?
对于每个节点u,用一个pre数组记录子树和为i的方案(类似背包求方案)
#include <bits/stdc++.h>
#define endl '\n'
#define x first
#define y second
#define ls(x) (a[x].l)
#define rs(x) (a[x].r)
#define sum(x) a[x].sum
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef double db;
const ll mod = 1e9 + 7;
const int N = 1e5 + 100, M = 25;
random_device rd;
mt19937_64 gen(rd());
ll t1[2], t2[2];
void solve()
{
int n;
cin >> n;
vector<char> c(n + 1);
vector<vector<int>> e(n + 1);
for (int i = 1; i <= n; i++)
cin >> c[i];
vector<vector<ll>> f(n + 1, vector<ll>(3, 0));
vector<ll> ans(n + 1);
for (int i = 2; i <= n; i++)
{
int x;
cin >> x;
e[x].push_back(i);
}
auto dfs = [&](auto dfs, int u) -> void
{
vector<ll> f1(3, 0), f2(3, 0);
f1[0] = 1;
for (auto to : e[u])
{
dfs(dfs, to);
for (int i = 0; i <= 2; i++)
f2[i] = 0;
for (int i = 0; i <= 2; i++)
for (int j = 0; j <= 2; j++)
if (f1[i] && f[to][j])
f2[(i + j) % 3] = 1;
swap(f1, f2);
}
for (int i = 0; i <= 2; i++)
for (int j = 1; j <= 2; j++)
if (f1[i])
f[u][(i + j) % 3] = j;
if (c[u] == 'R')
f[u][1] = f[u][2] = 0;
};
dfs(dfs, 1);
auto cal = [&](auto cal, int u, int s) -> void
{
ans[u] = f[u][s];
s = (s - f[u][s] + 3) % 3;
vector<ll> opt(e[u].size());
int now = 0;
vector<vector<ll>> pre(e[u].size(), vector<ll>(3, -1));
vector<ll> f1(3, 0), f2(3, 0);
f1[0] = 1;
for (auto to : e[u])
{
for (int i = 0; i <= 2; i++)
f2[i] = 0;
for (int i = 0; i <= 2; i++)
for (int j = 0; j <= 2; j++)
if (f1[i] && f[to][j])
{
f2[(i + j) % 3] = 1;
pre[now][(i + j) % 3] = j;
}
now++;
swap(f1, f2);
}
for (int i = now - 1; i >= 0; i--)
{
opt[i] = pre[i][s];
s = (s - opt[i] + 3) % 3;
}
now = 0;
for (auto to : e[u])
cal(cal, to, opt[now++]);
};
if (c[1] == 'R')
{
if (f[1][0])
{
cal(cal, 1, 0);
for (int i = 1; i <= n; i++)
cout << ans[i];
}
else
cout << "-1" << endl;
}
else
{
for (int i = 0; i <= 2; i++)
if (f[1][i])
{
cal(cal, 1, i);
for (int i = 1; i <= n; i++)
cout << ans[i];
return;
}
cout << "-1" << endl;
}
}
int main()
{
// cout << setprecision(5);
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int _ = 1;
// cin >> _;
while (_--)
solve();
return 0;
}
贪心做法:
先暴力把所有白色节点设置为1
对于以u为根的子树,
如果该节点为红色,且此时权值和为0:
如果有白色子节点,就修改为2
没有说明无解
如果权值和不为0,则可以根据权值和设置u的值
void solve()
{
int n;
cin >> n;
vector<vector<int>> e(n + 1);
vector<int> rev_dfn, ans(n + 1, 0), s(n + 1, 0);
vector<int> w(n + 1, 0);
vector<char> c(n + 1);
for (int i = 1; i <= n; i++)
cin >> c[i];
for (int i = 2; i <= n; i++)
{
int x;
cin >> x;
e[x].push_back(i);
if (c[i] == 'W')
w[x] = i;
}
for (int i = 1; i <= n; i++)
if (c[i] == 'R' && !w[i])
{
cout << "-1" << endl;
return;
}
auto dfs = [&](auto dfs, int u, int fa) -> void
{
for (auto to : e[u])
if (to != fa)
dfs(dfs, to, u);
rev_dfn.push_back(u);
};
dfs(dfs, 1, 0);
for (auto u : rev_dfn)
{
for (auto to : e[u])
s[u] += s[to];
if (c[u] == 'W')
ans[u] = 1, s[u]++;
else
{
if (s[u] % 3 == 0)
{
ans[u] = 2, s[u] += 3;
ans[w[u]] = 2, s[w[u]]++;
}
else if (s[u] % 3 == 1)
ans[u] = 2, s[u] += 2;
else
ans[u] = 1, s[u]++;
}
}
for (int i = 1; i <= n; i++)
cout << ans[i];
cout << endl;
}
K
考虑1*m的情况,暴力打表发现只有derder..
拓展到n*m,发现只有每行向左/右偏移一位
所有一共有\(A(3,3)*2\)种情况,计算每种情况需要修改的次数,查询时用二维前缀和优化
注意2*2还有类似一下情况
re
er
vector<string> list_s = {"red", "edr", "dre", "der", "erd", "rde"};
void solve()
{
int n, m, q;
cin >> n >> m >> q;
vector<vector<char>> a(n + 1, vector<char>(m + 1, '#'));
vector<vector<vector<int>>> v;
string s = "der";
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin >> a[i][j];
auto cal = [&](int d) -> vector<vector<int>>
{
vector<vector<int>> c(n + 1, vector<int>(m + 1, 0));
int p = 0;
for (int i = 1; i <= n; i++)
{
for (int j = 1, st = p; j <= m; j++, st = (st + 1) % 3)
{
c[i][j] = c[i - 1][j] + c[i][j - 1] - c[i - 1][j - 1] + (a[i][j] != s[st]);
}
p = (p + d + 3) % 3;
}
return c;
};
v.push_back(cal(1)), v.push_back(cal(-1));
while (next_permutation(s.begin(), s.end()))
v.push_back(cal(1)), v.push_back(cal(-1));
for (int i = 1; i <= q; i++)
{
auto cal = [&](int x1, int y1, int x2, int y2, vector<vector<int>> &s) -> int
{
return s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1];
};
int x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
int ans = 1e9;
for (auto &c : v)
ans = min(ans, cal(x1, y1, x2, y2, c));
if (x2 - x1 + 1 == 2 && y2 - y1 + 1 == 2)
{
for (auto &s : list_s)
{
char c1 = s[0], c2 = s[1];
int now = 0;
now += (a[x1][y1] != c1);
now += (a[x1][y2] != c2);
now += (a[x2][y1] != c2);
now += (a[x2][y2] != c1);
ans = min(ans, now);
}
}
cout << ans << endl;
}
}