代码狂欢复赛

复赛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. 简单三角形构造

牛客链接
现场思路没问题,但犯了两个失误(我的锅)

  1. 是求导错误, 凸函数最大值在 r 2 \frac{r}{2} 2r, 而不是 r ∗ 2 \sqrt{r*2} r2 …,这个很致命
  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:

  1. 考虑一个区间包含 [ l , r ] , r − l > = 2 [l, r],r - l >= 2 [l,r],rl>=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), 再聚合这两个珠子
  2. 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][gaplen]+alal+lenai+gap),len[1,gap)

    聚合中间点为 a i + l e n a_{i + len} ai+len

  3. 原始能量珠为环行,首尾聚合的问题可以转换为从任意起点开始到自身结束的区间聚合, 将原始数组在尾部递补,所有聚合方案均可由 [ 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")
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值