神话三峰子任务一题解

神话三峰子任务一题解

题目传送门

题意

第一问交互。
给你 \(N\) 个数的数组 \(H\) ,下标从 \(0\ 到\ N-1\)\(H[i] \in [1,N-1]\)
你要求符合 \(\{d(i,j),d(i,k),d(j,k)\}=\{h[i],h[j],h[k]\}\) 的三元组 \((i,j,k)\)个数。
规定 \(d(i,j)\) 表示 \(j-i\) 且上述约束条件是可重集。

实现细节

你要实现以下函数:

long long count_triples(std::vector<int> H)

\(H:\) 长度为 N 的数组,表示每座山峰的高度。
对每个测试用例,该函数恰好被调用一次。
该函数返回一个整数 T,表示满足上述条件的三元组数量。

思路

分情况讨论:

  • \(h[i]=max\)\(h[i]=k-i\) 那么 \(k=h[i]+i\)

    这个时候k已经知道,那么可以用 \(k\) 去推 \(i,j\) 分两种情况直接 \(check\)就可以。
    \(\{j-i,k-j\}=\{h[j],h[k]\}\)

    \[即: \ \ \ \begin{cases} k=h[i]+i \\ j=k-h[rnk2] \\ i=j-h[min] \end{cases} \ \ \ 或\ \ \ \begin{cases} k=h[i]+i;\\ j=k-h[min]\\ i=j-h[rnk2] \end{cases} \]

  • \(h[k]=max\ 则\ h[k]=k-i\ 那么\ k=h[k]+i\)
    同第一种情况一样做就可以。
    \(\{k-j,j-i\}=\{h[i],h[j]\}\)

    \[即: \ \ \ \begin{cases} k=h[k]+i\\ j=k-h[rnk2]\\ i=j-h[min] \end{cases} \ \ \ 或\ \ \ \begin{cases} k=h[k]+i\\ j=k-h[min]\\ i=j-h[rnk2] \end{cases} \]

  • \(h[j]=max\) 是困难的,此时 \(k-i=h[j]\)

    • 但是当 \(j-i=h[i],k-j=h[k]\) 是可做的。
      考虑变形 \(j=i+h[i]=k-h[k]\)
      处理出来 \(l[j(j=i+h[i])].pb(i),r[j(k-h[k])].pb(k)\)

      枚举 \(j\),然后枚举右端点 \(r[j]\ 即\ k\) 给右端点打上 \(vis\) 标记。
      再枚举左端点 \(l[j]\ 即\ i\) 利用上最开始的条件 \(k=i+h[j]\)\(vis[i+h[j]]\)
      如果右端点被标记了就符合条件。


      复杂度分析

      这个东西复杂度看起来很像\(O(n^2)\),但是其实因为 \(\sum_{i}l_i,\sum_{i}r_i<=n\) ,均摊下来是 \(O(n)\) 的复杂度。


      但是计数的时候要注意这里有坑。

      如果 \(k-j=j-i\) 的情况在这里会统计两次,那么考虑坚决方法,我们知道 \(k-j=h[k],j-i=h[i]\) 那么在上面右端点被标记并且要求 \(h[k]!=h[i]\) 的时候可以直接使 \(ans\)++。

      对于 \(h[k]=h[i]\) 的情况单独统计一遍,还是枚举 \(j\) ,再枚举长度 \(d\) ,限制 j-d>=1 && j+d<=n 那么 if(h[i]==d && h[k]==d && h[j]==2*d) ans++;

    • 最麻烦的是 \(j-i=h[k],k-j=h[i]\) 的情况。
      考虑变形 \(\color{red}i+h[k]=k-h[i]=j\)
      可以得到第一个式子:

      1. \(i+h[k]=k-h[i]\)

      然后把最开始的条件 \(k-i=h[j]\) 带入\(\color{red}{红色式子}\)消元:

      1. \(j+h[i]=i+h[j]\)
      2. \(k-h[j]=j-h[k]\)

      整理式子可得:

      \[\begin{cases} i+h[i]=k-h[k]\\ j-h[j]=i-h[i]\\ k+h[k]=j+h[j] \end{cases} \]

      对于这个情况我们直接进行三元环计数即可。
      具体来说我们用 \(u_x\) 表示 \(x+h[x]\)\(v_x\) 表示 \(x-h[x]\)
      上述条件就变成了:

      \[\begin{cases} u_i=v_k\\ v_j=v_i\\ u_k=u_j \end{cases} \]

      直接把 \(u_x,v_x\) 的值当点。
      建图的时候直接对于 \(u_x\)\(v_x\) 连无向边,进行三元环计数就是对的。
      考虑为什么三元环计数是对的。
      按照我们的建图方式,三元环计数统计找出的三元环,已经满足了每个点分别是上述三个等式的值,限制条件是完备的,所以 \(ans\)+=\(sum\) 就可以了。(\(sum\)就是三元环计数的结果),就像下面这个图一样,我们找出来的三元环就是满足约束的一组 \((i,j,k)\)
      img

以上就是本题的所有分析,其实是分析六种情况然后分类讨论,难点在于最后一种大情况的式子整理和转化成三元环计数。

code

#include<bits/stdc++.h>
#define pb push_back
using namespace std;

const int N = 4e5+10;
typedef long long ll;

int h[N>>1];
vector<int> l[N>>1], r[N>>1];
int vis[N];
int deg[N], rnk[N];
vector<int> g[N];
vector<pair<int,int>> e;
map<int,int> id;
int tot=0;

void init(){
    e.clear();
    id.clear();
    tot=0;
    memset(vis,0,sizeof(vis));
    memset(deg,0,sizeof(deg));
    memset(rnk,0,sizeof(rnk));
    for(int i=0;i<N>>1;i++){
        l[i].clear();
        r[i].clear();
    }
    for(int i=0;i<N;i++){
        g[i].clear();
    }
}

ll count_triples(vector<int> H){
    init();
    int n=H.size();
    for(int i=1;i<=n;i++) h[i]=H[i-1];
    ll ans=0;

    for(int i=1;i<=n;i++){
        int k=i+h[i];
        if(k>n) continue;
        int j=i+h[k];
        if(j<k && j+h[j]==k) ans++;
        if(k-h[k]!=j){
            j=k-h[k];
            if(i<j && j-h[j]==i) ans++;
        }
    }

    for(int k=1;k<=n;k++){
        int i=k-h[k];
        if(i<1) continue;
        int j=i+h[i];
        if(j<k && j+h[j]==k) ans++;
        if(k-h[i]!=j){
            j=k-h[i];
            if(i<j && j-h[j]==i) ans++;
        }
    }

    for(int i=1;i<=n;i++){
        if(i+h[i]<=n) l[i+h[i]].pb(i);
        if(i-h[i]>=1) r[i-h[i]].pb(i);
    }
    for(int i=1;i<=n;i++){
        for(auto u:r[i]) vis[u]=1;
        for(auto u:l[i]){
            if(u+h[i]<=n && vis[u+h[i]] && h[u+h[i]]!=h[u]) ans++;
        }
        for(auto u:r[i]) vis[u]=0;
    }

    for(int i=1;i<=n;i++){
        int h1=i+h[i];
        int h2=i-h[i];
        if(id.find(h1)==id.end()) id[h1]=++tot;
        if(id.find(h2)==id.end()) id[h2]=++tot;
        h1=id[h1]; h2=id[h2];
        e.pb({h1,h2});
        deg[h1]++; deg[h2]++;
    }

    vector<int> p(tot+1);
    for(int i=1;i<=tot;i++) p[i]=i;
    sort(p.begin()+1,p.end(),[&](int x,int y){
        return deg[x]<deg[y] || (deg[x]==deg[y] && x<y);
    });
    for(int i=1;i<=tot;i++) rnk[p[i]]=i;

    for(auto [u,v] : e){
        if(rnk[u]<rnk[v]) g[u].pb(v);
        else g[v].pb(u);
    }

    for(int u=1;u<=tot;u++){
        for(auto v:g[u]) vis[v]=u;
        for(auto v:g[u]){
            for(auto w:g[v]){
                if(vis[w]==u) ans++;
            }
        }
    }

    return ans;
}

posted @ 2026-08-03 22:56  词印  阅读(18)  评论(0)    收藏  举报