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;
    }
}
posted @ 2024-02-26 22:18  0x3ea  阅读(3)  评论(0编辑  收藏  举报