[集训队互测 2025] 携春同行 题解
赛场上没调完狂砍
提供一种非常简洁的做法。是题解发布时的 QOJ 最短解。
先尝试做
问题在于目前无法区分谁是第三大值——特殊性质下的判断方法很多且与正解无关,在此略过。直接考虑没有特殊性质时的方法,发现此时与有特殊性质的唯一区别在于,可能有很多个元素的值
考虑一种暴力地判断备选元素是否合法的方法:把需要被判断的元素拿出来,其他元素连成一条链,并且这条链满足头尾均为备选元素。再把这个元素对应的点和链的中点连边。这样如果这个元素
尝试拓展这个方法,此时在中点上挂不止一个点,这样就能判断里头是否存在不合法的元素(如果 check
但是此时要保证至少有两个点挂在链的头尾,发现二分最多 check
放代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<ll> query(const vector<vector<int> > &U,const vector<vector<int> > &V);
bool guess(const vector<int> &U,const vector<int> &V,ll x);
vector<int> haru(int n){
vector<vector<int> > U(n+1),V(n+1);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
if(i!=j)U[i].push_back(i),V[i].push_back(j); // 构造菊花
for(int i=0;i<n-1;i++)
U[n].push_back(i),V[n].push_back(i+1); // 构造链
auto R=query(U,V);
vector<ll> v(R.begin(),R.begin()+n);
sort(v.begin(),v.end());
ll t=v.back(),q=t-(accumulate(v.begin(),v.begin()+n-3,0ll)-(R[n]-t))/(n-3); // 解出 q 的值
vector<int> a(n),p;
for(int i=0;i<n;i++)
if((a[i]=R[i]-t+q)==q)p.push_back(i); // 筛出备选元素
auto check=[&](int l,int r){
vector<bool> b(n);
for(int i=l;i<=r;i++)
b[p[i]]=true;
int h=p[(l+p.size()-1)%p.size()],t=p[(r+1)%p.size()];
vector<int> c={h},U,V;
for(int i=0;i<n;i++)
if(i!=h&&i!=t&&!b[i])c.push_back(i);
c.push_back(t);
for(int i=0;i+1<c.size();i++)
U.push_back(c[i]),V.push_back(c[i+1]);
for(int i=0;i<n;i++)
if(b[i])U.push_back(c[c.size()>>1]),V.push_back(i);
return !guess(U,V,R[n]-1ll*(r-l+1)*q);
}; // 判断 p[l...r] 内是否有不合法元素
if(p.size()<=18){
for(int i=0;i<p.size();i++)
if(check(i,i))a[p[i]]=-1;
return a;
} // 备选元素不多直接暴力
auto f=[&](auto &&sf,int l,int r)->int{
if(l>=r)return r;
int m=l+r>>1;
return check(l,m)?sf(sf,l,m):sf(sf,m+1,r);
}; // 使用递归实现二分
int x=f(f,0,p.size()-1),y=f(f,x+1,p.size()-1);
return a[~x?p[x]:0]=a[~y?p[y]:0]=-1,a;
}