题解|2024暑期牛客多校04

【原文链接】

比赛链接:2024牛客暑期多校训练营4

A.LCT

题目大意

给定一棵有根树,问按顺序给定的前 i i i 条边组成的森林中,以 c i c_i ci 为根的树的深度。

解题思路

按步骤生成森林的过程,与并查集合并的过程一致。
因此用带权并查集,维护每个点的深度和答案,利用路径压缩降低时间复杂度。

参考程序

struct DSU{
    vector<ll> parents, size, dep, ans;
    explicit DSU(ll n) : parents(n + 1), size(n + 1, 1), dep(n + 1, 0), ans(n + 1, 0) { iota(parents.begin(), parents.end(), 0); }
    ll find(ll x) {
        if(x == parents[x]) return x;
        ll px = find(parents[x]);
        dep[x]+=dep[parents[x]]; 
        return parents[x]=px;
    }
    void merge(ll a, ll b)
    { // merge b into a
        ll pa = find(a);
        parents[b] = a;
        dep[b] = 1;
        find(b);
        chmax(ans[pa],ans[b]+dep[b]);
    }
    ll query(ll x) {return ans[x];}
};

void solve()
{
    ll n;cin >> n;
    DSU dsu(n);
    ll a,b,q;
    FORLL(i,1,n-1){
        cin >> a >> b;
        dsu.merge(a,b);
        cin >> q;
        cout << dsu.query(q) << Presentation(i,n-1);
    }
}

C.Sort4

题目大意

给定一个长度为 n n n 的排列 p p p (即序列中 1 1 1 ~ n n n 的每个数恰好出现一次)。
每次操作可以选择4个元素,并任意交换它们的位置。
求使得排列变为升序的最少操作次数。

解题思路

把排列看作由 i → p i i\rightarrow p_i ipi(下标从1开始) 构成的图,这个图中有若干个环,表示这个环中的元素可以通过交换回到原来的位置。

长度为 3 , 4 3,4 3,4 的环,可以通过一次操作还原;
长度为 2 2 2 的环,可以 2 2 2 个环一组通过一次操作还原;
长度大于 4 4 4 的环,每次操作可以让 3 3 3 个元素回到原来的位置,使得环的长度减少 3 3 3,直到环的长度小于等于 4 4 4

根据这个原则,计算最终答案。

参考程序

void solve()
{
    ll n;cin >> n;
    vector<ll> v(n+1);
    FORLL(i,1,n) cin >> v[i];
    vector<int> vis(n+1,0);
    ll ans=0,of=0; //of:最终长度为2的环的数量
    map<ll,ll> mp;
    FORLL(i,1,n){
        if(vis[v[i]]) continue;
        ll cnt=1,cur=v[i];
        vis[i]=1;
        while(cur!=i){
            vis[cur]=1;
            cur = v[cur];
            cnt++;
        }
        if(cnt==1) continue;
        if(cnt%3==1) ans+=(cnt-1)/3; //最终长度为4的环
        else if(cnt%3==2){ //最终长度为2的环
            ans+=(cnt-2)/3;
            of++;
        }else ans+=cnt/3; //最终长度为3的环
    }
    cout << ans+(of+1)/2 << endl;
}

H.Yet Another Origami Problem

题目大意

给定一个长度为 n n n 的序列 a a a,每次可以选择一个元素 a i a_i ai,执行以下操作之一:

  1. 所有原来比 a i a_i ai 小的数 a j a_j aj a j = a i + 2 ( a i − a j ) a_j=a_i+2(a_i-a_j) aj=ai+2(aiaj) ,即原来 a j a_j aj a i a_i ai 小多少,现在就比 a i a_i ai 大多少。
  2. 所有原来比 a i a_i ai 大的数 a j a_j aj a j = a i − 2 ( a j + a i ) a_j=a_i-2(a_j+a_i) aj=ai2(aj+ai) ,即原来 a j a_j aj a i a_i ai 大多少,现在就比 a i a_i ai 小多少。

问任意次操作后,序列 a a a 中最大元素和最小元素之差 m a x ( a ) − m i n ( a ) max(a)-min(a) max(a)min(a) 的最小值。

解题思路

每次操作:

  1. 对序列 a a a 排序去重,求出差分数组 d d d
  2. 选定次小元素 a 2 a_2 a2 ,执行操作 1 1 1 ,使得最小元素 a 1 = a 2 − d 1 a_1 = a_2 - d_1 a1=a2d1 变成 a 1 ′ = a 2 + d 1 a_1' = a_2 + d_1 a1=a2+d1

假设重新排序去重后,新的 a 1 ′ a_1' a1 相邻的两项为 a j , a j + 1 a_j,a_{j+1} aj,aj+1 ,步骤1中这两项的差分是 d j = a j + 1 − a j d_j=a_{j+1}-a_j dj=aj+1aj
那么当 a i ′ a_i' ai 插到中间时,新的差分数组的变化如下:

  1. 第1项 d 1 d_1 d1 删去(因为 a 1 a_1 a1 变成了 a 1 ′ a_1' a1 后移)
  2. 原本的 d j d_j dj 被替换为 d j ′ = d 1 − ∑ k = 2 j − 1 d k d_j'=d_1-\sum\limits_{k=2}^{j-1}d_k dj=d1k=2j1dk d j + 1 ′ = d j − d j ′ d'_{j+1}=d_j-d_j' dj+1=djdj

第2点变化可能比较难理解,给出如下例子:

  • a = [ 1 , 6 , 8 , 10 , 13 , 15 ] a = [1,6,8,10,13,15] a=[1,6,8,10,13,15] (原序列排序去重)
  • d = [ 5 , 2 , 2 , 3 , 2 ] d = [5,2,2,3,2] d=[5,2,2,3,2] (差分数组)
  • a 1 = 1 , a 2 = 6 , d 1 = 5 a_1=1,a_2=6,d_1=5 a1=1,a2=6,d1=5 (选定次小元素做操作1)
  • a 1 ′ = a 2 + d 1 = 11 a_1'=a_2+d_1=11 a1=a2+d1=11
  • a ′ = [ 6 , 8 , 10 , 11 , 13 , 15 ] a' = [6,8,10,11,13,15] a=[6,8,10,11,13,15] (新序列排序去重, a 1 ′ a_1' a1 位于第4位)
  • d ′ = [ 2 , 2 , 1 , 2 , 2 ] d' = [2,2,1,2,2] d=[2,2,1,2,2] (新差分数组)

差分数组的变化:

  1. 第1项 d 1 = 5 d_1=5 d1=5 删去
  2. 第4项 3 3 3 变为: d 1 − d 2 − d 3 = 1 d_1-d_2-d_3=1 d1d2d3=1 3 − 1 = 2 3-1=2 31=2

这个变化的意义就在于,它证明了任意次操作后的差分数组中的元素,是原差分数组中元素的线性组合,且随着操作次数增加, ∑ d i \sum d_i di 逐渐减小,直到 a a a 仅剩2个元素。
线性组合能达到的最小值为 gcd ⁡ i = 1 n − 1 d i \gcd\limits_{i=1}^{n-1}d_i i=1gcdn1di

参考程序

void solve()
{
    ll n;cin >> n;
    create_vec(v,n);
    SORT(v);
    ll ans = 0;
    FORLL(i,1,n-1)
        ans = __gcd(ans,v[i]-v[i-1]);
    cout << ans << endl;
}

G.Horse Drink Water

题目大意

将军饮马问题,将军在第一象限的整点 ( x 0 , y 0 ) (x_0,y_0) (x0,y0) ,河流由 x x x 正半轴和 y y y 正半轴组成。
问将军碰到河流再前往 ( x 1 , y 1 ) (x_1,y_1) (x1,y1) 的最短路径长度。

解题思路

将起点以 x x x 轴、 y y y 轴为对称轴,分别对称到第四、二象限,比较这两个点和终点的距离,取最小值。

参考程序

void solve()
{
    ld xx1,xx2,yy1,yy2;
    cin >> xx1 >> yy1 >> xx2 >> yy2;
    ld ans1,ans2;
    ans1 = sqrt((xx1-xx2)*(xx1-xx2)+(yy1+yy2)*(yy1+yy2));
    ans2 = sqrt((xx1+xx2)*(xx1+xx2)+(yy1-yy2)*(yy1-yy2));
    print_float(min(ans1,ans2),10);
    cout << endl;
}

I

// TODO

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深翼CCLMSY

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值