2024牛客暑期多校训练营4

A. LCT

赛时三个人都写不来带权值的并查集,经过两个队友的不懈努力终于调出来了~~(骄傲)~~。

带权值的并查集,维护每个节点到祖先的距离 h [ x ] h[x] h[x],每次更新祖先路径长的每个点的答案为 a n s [ f a [ x ] ] = m a x ( a n s [ f a [ x ] ] , a n s [ x ] + h [ x ] ) ans[fa[x]] = max(ans[fa[x]],ans[x]+h[x]) ans[fa[x]]=max(ans[fa[x]],ans[x]+h[x]),每次查询输出那个点的 a n s − 1 ans-1 ans1 即可,时间复杂度不太好算。

想学下带权值并查集的话可以参照一下这篇博客,写的很详细。

带权并查集_带权并查集jiangly-CSDN博客

#include <bits/stdc++.h>
#define int long long
using namespace std;
#define YES "YES"
#define NO "NO"
#define all(a) a.begin(), a.end()
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<int, int> pii;
const double eps = 1e-9;
const int N = 1e6 + 10;
const int INF = 1e18 + 100;
const int mod = 998244353;
const int base = 23333;
int fa[N], h[N], ans[N];
int find(int x)
{
    if(x != fa[x])
    {
        int t = fa[x];
        ans[t] = max(ans[t], ans[x] + h[x]);
        fa[x] = find(t);
        h[x] += h[t];
    }
    return fa[x];
}
void solve()
{
    int n;
    cin >> n;
    for(int i = 1;i <= n;i++)
    {
        fa[i] = i;
        ans[i] = 1;
        h[i] = 0;
    }
    for(int i = 1;i < n;i++)
    {
        int u, v, q;
        cin >> u >> v >> q;
        fa[v] = u;
        h[v] = 1;
        find(v);
        cout << ans[q] - 1 << " ";
    }
    cout << '\n';
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) solve();
    return 0;
}

C. Sort4

根据给出的 i − > a i i->a_i i>ai 可以构成多个环,通过遍历这些环可以得到环的规模 r e s res res ,在纸上模拟一下可以知道 r e s % 3 = 2 res\%3=2 res%3=2 的环需要 r e s / 3 res/3 res/3 次的操作数,操作完之后还会剩下一个 r e s res res 2 2 2 的环, r e s % 3 ≠ 2 res\%3\neq2 res%3=2 的环经过 r e s / 3 res/3 res/3 次操作后可以全部解决。

那我们只用关注 r e s = 2 res=2 res=2 的环了,不难发现,一次操作可以同时去除掉两个 r e s = 2 res=2 res=2 的环,所以处理总的环和处理 r e s = 2 res=2 res=2 的环次数之和即为所求,时间复杂度 O ( n ) O(n) O(n)

#include <bits/stdc++.h>
#define int long long
using namespace std;
#define YES "YES"
#define NO "NO"
#define all(a) a.begin(), a.end()
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const double eps = 1e-9;
const int N = 1e6 + 10;
const int INF = 1e18 + 100;
const int mod = 998244353;
const int base = 23333;
int fa[N], vis[N];
void solve()
{
    int n;
    cin >> n;
    for(int i = 1;i <= n;i++)
    {
        fa[i] = i;
        vis[i] = 0;
    }
    for(int i = 1;i <= n;i++)
    {
        int u;
        cin >> u;
        fa[i] = u;
    }
    int ans = 0, sum = 0;
    for(int i = 1;i <= n;i++)
    {
        int res = 0, now = i;
        while(!vis[now])
        {
            vis[now] = 1;
            now = fa[now];
            res++;
        }
        ans += res / 3;
        if(res % 3 == 2) sum++;
    }
    ans += (sum + 1) / 2;
    cout << ans << '\n';
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) solve();
    return 0;
}

F. Good Tree

通过简单的数学运算可以得到对于节点数为 n n n 的树,它的 m a x f ( u ) − f ( v ) max_{f(u)-f(v)} maxf(u)f(v) 的值为 ( n − 1 ) 2 4 \frac{(n-1)^2}{4} 4(n1)2 ,所以很简单啊,直接二分找到最小的 n n n 就是答案嘛,然后就喜提了好几发 w a wa wa,经过几个小时的研究之后,终于发现了原来 n n n 为偶数的时候 f ( i ) f(i) f(i) 都为偶数,而 n n n 为奇数的时候 f ( i ) f(i) f(i) 中有奇数和偶数,偶数减偶数是不会得到偶数的 x x x 的,所以 x x x 为奇数和 n n n 为偶数的时候输出 n + 1 n+1 n+1,时间复杂度 O ( t l o g x ) O(tlogx) O(tlogx)。(别问怎么证,问就是玩数据玩了好几个小时)

#include <bits/stdc++.h>
#define int long long
using namespace std;
#define YES "YES"
#define NO "NO"
#define all(a) a.begin(), a.end()
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<int, int> pii;
const double eps = 1e-9;
const int N = 1e6 + 10;
const int INF = 1e18 + 100;
const int mod = 998244353;
const int base = 23333;
void solve()
{
    int x;
    cin >> x;
    int l = 1, r = 1e16;
    int ans = INF;
    while(l < r)
    {
        //cout << l << " " << r << '\n';
        __int128 n = (l + r) / 2;
        __int128 k = (n - 1) / 2;
        __int128 now = -k * k + (n - 1) * k;
        if(now >= x)
        {
            r = n;
            ans = min(ans, r);
        }
        else l = n + 1;
    }
    if(x % 2 == 0) cout << ans << '\n';
    else
    {
        if(ans % 2 == 0) cout << ans + 1 << '\n';
        else cout << ans << '\n';
    }
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) solve();
    return 0;
}

G. Horse Drinks Water

很经典的一个问题,我也叫不上来是什么,反正就是马的坐标对应到坐标轴的另一边,然后对应后的坐标和目的地连线,这条线就是所求的最小, x x x 轴和 y y y 轴计算结果取小即可,时间复杂度 O ( t ) O(t) O(t)

#include <bits/stdc++.h>
#define int long long
using namespace std;
#define YES "YES"
#define NO "NO"
#define all(a) a.begin(), a.end()
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<int, int> pii;
const double eps = 1e-9;
const int N = 5e5 + 10;
const int INF = 1e18 + 100;
const int mod = 998244353;
const int base = 23333;
void solve()
{
    ld x1, y1, x2, y2;
    cin >> x1 >> y1 >> x2 >> y2;
    double t1 = sqrt((y2 - y1) * (y2 - y1) + (x2 + x1) * (x2 + x1));
    double t2 = sqrt((y2 + y1) * (y2 + y1) + (x2 - x1) * (x2 - x1));
    cout << fixed << setprecision(10) << min(t1, t2) << '\n';
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) solve();
    return 0;
}

H. Yet Another Origami Problem

其实这题也是手玩出来的,这场是什么手玩场吗(

因为元素的顺序对结果没影响,所以先对所有的元素进行升序排序,最后的答案就是 g c d ( a 2 − a 1 , a 3 − a 2 , … , a n − a n − 1 ) gcd(a_2-a_1,a_3-a_2,\dots,a_n-a_{n-1}) gcd(a2a1,a3a2,,anan1),玩下题目给出的数据和 3 , 6 , 10 3,6,10 3,6,10 这组数据基本上都能出来,时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

#include <bits/stdc++.h>
#define int long long
using namespace std;
#define YES "YES"
#define NO "NO"
#define all(a) a.begin(), a.end()
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<int, int> pii;
const double eps = 1e-9;
const int N = 5e5 + 10;
const int INF = 1e18 + 100;
const int mod = 998244353;
const int base = 23333;
int a[N];
void solve()
{
    int n;
    cin >> n;
    for(int i = 1;i <= n;i++) cin >> a[i];
    sort(a+1,a+n+1);
    int now = 0;
    for(int i = 2;i <= n;i++)
    {
        int t = a[i] - a[i - 1];
        if(t == 0) continue;
        if(now == 0) now = t;
        else now = __gcd(now, t);
    }
    cout << now << '\n';
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) solve();
    return 0;
}

I. Friends

如果一个 [ l , r ] [l,r] [l,r] 区间是友好的会有什么性质?

即对每个 i ( l ≤ i ≤ r ) i(l \leq i \leq r) i(lir) j ( i ≤ j ≤ r ) j(i \leq j \leq r) j(ijr) 都满足 i i i j j j 是一对朋友,所以我们考虑以一个人为左端点的时候满足和当前这个人都为朋友的右区间最大长度,记每个人的最右边区间端点为 r [ i ] r[i] r[i] ,由于友好区间取交集,所以从后往前遍历, r [ i ] = m i n ( r [ i ] , r [ i + 1 ] ) r[i]=min(r[i],r[i+1]) r[i]=min(r[i],r[i+1]),最后统计每个 r [ i ] r[i] r[i] 的贡献, ∑ i = 1 n r [ i ] − i + 1 \sum_{i=1}^{n}r[i]-i+1 i=1nr[i]i+1 即为所求,时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

#include <bits/stdc++.h>
#define int long long
using namespace std;
#define YES "YES"
#define NO "NO"
#define all(a) a.begin(), a.end()
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<int, int> pii;
const double eps = 1e-9;
const int N = 1e6 + 10;
const int INF = 1e18 + 100;
const int mod = 998244353;
const int base = 23333;
vector<int> e[N];
int r[N];
void solve()
{
    int n, m;
    cin >> n >> m;
    for(int i = 1;i <= m;i++)
    {
        int u, v;
        cin >> u >> v;
        e[u].push_back(v);
        e[v].push_back(u);
    }
    for(int i = 1;i <= n;i++) sort(all(e[i]));
    for(int i = 1;i <= n;i++)
    {
        int now = i;
        r[i] = i;
        for(auto u : e[i])
        {
            if(u < now) continue;
            if(u == now + 1)
            {
                now++;
                r[i] = u;
            }
            else break;
        }
    }
    for(int i = n - 1;i >= 1;i--) r[i] = min(r[i], r[i + 1]);
    int ans = 0;
    for(int i = 1;i <= n;i++) ans += (r[i] - i + 1);
    cout << ans << '\n';
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    //cin >> t;
    while (t--) solve();
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值