复赛Solution
必做6. 滑动窗口中位数
牛客链接
离散化树状数组 + 二分,现场没有加离散化超时
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param nums int整型vector
* @param k int整型
* @return double浮点型vector
*/
static const int N = 100010;
int c[N];
int n;
vector<int> pos;
int lowbit(int x) {
return x & (-x);
}
void update(int x, int v) {
while(x <= n) {
c[x] += v;
x += lowbit(x);
}
}
int get(int x) {
int res = 0;
while(x) {
res += c[x];
x -= lowbit(x);
}
return res;
}
int binary_search(int k) {
int l = 1, r = n;
while(l < r) {
int m = (l + r) >> 1;
if(get(m) < k) {
l = m + 1;
} else {
r = m;
}
}
return pos[r - 1];
}
double query(int k) {
if (k & 1) {
return binary_search(k / 2 + 1);
}
return (binary_search(k / 2 + 1) + binary_search(k / 2)) / 2.0;
}
vector<double> slidewindow(vector<int>& nums, int k) {
pos = nums;
sort(pos.begin(), pos.end());
pos.erase(unique(pos.begin(), pos.end()), pos.end()); // 离散化
n = pos.size();
vector<double> res(nums.size() - k + 1);
for (int i = 0; i < k; ++i) {
int t = lower_bound(pos.begin(), pos.end(), nums[i]) - pos.begin() + 1; // lower_bound获取离散化索引
update(t, 1);
}
int cnt = 0;
res[cnt++] = query(k);
for (int i = k; i < nums.size(); ++i) {
int t1 = lower_bound(pos.begin(), pos.end(), nums[i]) - pos.begin() + 1;
int t2 = lower_bound(pos.begin(), pos.end(), nums[i - k]) - pos.begin() + 1;
update(t1, 1);
update(t2, -1);
res[cnt++] = query(k);
}
return res;
}
};
选做3. 简单三角形构造
牛客链接
现场思路没问题,但犯了两个失误(我的锅)
- 是求导错误, 凸函数最大值在 r 2 \frac{r}{2} 2r, 而不是 r ∗ 2 \sqrt{r*2} r∗2…,这个很致命
- 太久没刷题,忘记控制输出精度到小数点后7位(对应setprecision)
现场的时候有考虑到圆点和P重合没法取反余弦要特判的问题,但是没写
#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-7;
struct Point {
double x;
double y;
Point(){}
Point(double x, double y) : x(x), y(y) {}
};
double dist(Point a) {
return sqrt(a.x * a.x + a.y * a.y);
}
Point AB(Point a, Point b) {
return Point(b.x- a.x, b.y -a.y);
}
double dist(Point a, Point b) {
return dist(AB(a, b));
}
Point elem(Point a) {
double d =dist(a);
return Point(a.x / d, a.y /d);
}
int main() {
double r, S;
Point p, p0;
cin >> r >> p.x >> p.y >> S >> p0.x >> p0.y;
double d = dist(p, p0);
double mx_d = min(r / 2,d);
double mx_area = (r + mx_d) * sqrt(r * r - mx_d * mx_d);
if (mx_area <S) {
cout << -1 << endl;
return 0;
}
if(mx_d < eps) { // 圆点和P重合,直接构造一个等腰三角形
cout << fixed << setprecision(7)
<< p.x << ' ' << p.y - r << ' '
<< p.x - r<< ' ' << p.y << ' '
<< p.x + r<< ' ' << p.y << ' '
<< endl;
return 0;
}
Point a = elem(AB(p,p0));
double theta = acos(mx_d / d);
Point b;
b.x = a.x * cos(theta) - a.y * sin(theta);
b.y = a.y * cos(theta) + a.x * sin(theta);
double xa, ya;
xa = p.x-r * b.x;
ya = p.y-r * b.y;
double xt = p.x + mx_d * b.x;
double yt = p.y + mx_d * b.y;
Point c(-b.y, b.x);
double dt= sqrt(r * r - mx_d * mx_d);
double xb,yb,xc,yc;
xb = xt - dt * c.x;
xc = xt + dt * c.x;
yb = yt - dt * c.y;
yc = yt + dt * c.y;
cout << fixed << setprecision(7)<< xa << ' ' << ya << ' ' << xb << ' ' << yb << ' ' << xc << ' ' << yc << endl;
return 0;
}
选做6. 白魔法师
牛客链接
并查集, 思路就是对于每个黑色的点,染色后的最大连通块大小为1 + 所有邻接点的连通块大小之和, 连通块用并查集处理(两个相邻结点均为白色时加入并查集,此题并查集需要优化否则会超时), 现场的时候邻接点只建了单边。。。
#include <iostream>
#include <vector>
using namespace std;
const int N = 100010;
int fa[N];
int cnt[N];
int getfa(int x) {
while(fa[x] != x) x = fa[x];
return x;
}
vector<int> g[N];
void add(int x, int y) {
int fx = getfa(x);
int fy = getfa(y);
if (fx != fy) {
if (cnt[fx] > cnt[fy]) {
fa[fy] = fx;
cnt[fx] += cnt[fy];
} else {
fa[fx] = fy;
cnt[fy] += cnt[fx];
}
}
}
int main() {
int n; cin >> n;
string s; cin >> s;
for (int i = 1; i <= n; ++i) {
cnt[i] = s[i - 1] == 'W';
fa[i] = i;
}
for (int i = 0; i < n - 1; ++i) {
int a, b;
cin >> a >> b;
g[a].push_back(b);
g[b].push_back(a);
if (s[a - 1] == 'W' && s[b - 1] == 'W') {
add(a, b);
}
}
int res = 0;
for (int i = 1; i <= n; ++i) {
if (s[i - 1] == 'B') {
int cur = 1;
for (auto& u : g[i]) {
cur += cnt[getfa(u)];
}
res = max(res, cur);
}
}
if (res == 0) {
res = n;
}
cout << res << endl;
return 0;
}
选做5. 能量项链
本题数据貌似有问题,我倾向于标程是下面这个, 牛客同名的几道题在dp的过程中取模不是全局最优,防止溢出应该是要long long
思路是区间DP:
- 考虑一个区间包含 [ l , r ] , r − l > = 2 [l, r],r - l >= 2 [l,r],r−l>=2的能量珠集合, 先不考虑首尾聚合的场景,将其聚合成 ( a l , a r ) (a_l, a_r) (al,ar)这种珠子,聚合时必然存在一个 m , m ∈ ( l , r ) m, m \in (l, r) m,m∈(l,r), 分别将 [ l , m ] 、 [ m , r ] [l, m]、[m, r] [l,m]、[m,r]两个字区间分别聚合成 ( a l , a m ) 和 ( a m , a r ) (a_l, a_m)和(a_m, a_r) (al,am)和(am,ar), 再聚合这两个珠子
- 令
d
p
[
i
]
[
g
a
p
]
dp[i][gap]
dp[i][gap]为
[
i
,
i
+
g
a
p
]
[i, i + gap]
[i,i+gap]区间聚合成
(
a
i
,
a
i
+
g
a
p
)
(a_i, a_{i + gap})
(ai,ai+gap)获取的最大能量值, 则有状态转移方程
d p [ i ] [ g a p ] = m a x ( d p [ i ] [ l e n ] + d p [ i + l e n ] [ g a p − l e n ] + a l ∗ a l + l e n ∗ a i + g a p ) , l e n ∈ [ 1 , g a p ) dp[i][gap] = max(dp[i][len] + dp[i+len][gap-len] + a_l * a_{l+len} * a_{i + gap}), len \in [1, gap) dp[i][gap]=max(dp[i][len]+dp[i+len][gap−len]+al∗al+len∗ai+gap),len∈[1,gap)聚合中间点为 a i + l e n a_{i + len} ai+len
- 原始能量珠为环行,首尾聚合的问题可以转换为从任意起点开始到自身结束的区间聚合, 将原始数组在尾部递补,所有聚合方案均可由
[
i
,
i
+
n
]
,
i
∈
[
1
,
n
]
其中之一表示
[i, i + n], i\in[1, n]其中之一表示
[i,i+n],i∈[1,n]其中之一表示
a 1 , a 2 , . . . . a n , a 1 , a 2 , . . . , a n a_1, a_2, .... a_n, a_1, a_2, ..., a_n a1,a2,....an,a1,a2,...,an
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
const long long mod = 1e9 + 7;
long long dp[N * 2][N];
long long a[N * 2];
int main() {
int n; cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
a[i + n] = a[i];
}
for (int gap = 2; gap <= n; ++gap)
for (int i = 1; i + gap <= 2 * n; ++i)
for (int j = 1; j < gap; ++j)
dp[i][gap] = max(dp[i][gap], (dp[i][j] + dp[i + j][gap - j] + a[i] * a[i + j] * a[i + gap]) );
long long res = 0;
for (int i = 1; i <= n; ++i) {
res = max(res, dp[i][n]);
}
cout << res % mod << endl;
return 0;
}
// 64 位输出请用 printf("%lld")
346






