洛谷的AtC接口貌似坏掉了,水不了这道紫题了,但是题解还是可以水的(大雾
洛谷题目页面传送门 & AtC题目页面传送门
有(n)个点,第(i)个点的权值为(a_i)。给定常数(d),在点(i,j)之间连边的代价是(|i-j|d+a_i+a_j)。求将它们连成一棵树的最小代价。
(ninleft[1,10^5 ight])。
首先不难发现,原题相当于,求完全图的MST的权,其中点(i,j)之间的边权为(|i-j|d+a_i+a_j)。如果直接Kruskal的话,建图就要爆炸,边数是(mathrm O!left(n^2 ight))的。考虑用某些MST的奇技淫巧。
我们的目的是,尽可能删掉一些边,留下尽量少的边,使得MST的权不变。
注意到这样一条经典的性质:MST不可能包含一个环上所有的最大边。证明很简单:反证法。若包含,则随意去掉一个环上的最大边((x,y)),此时整棵树断成两部分,(x,y)在不同的部分。由于是个环,那么环上一定有一条反向的不包含 ((x,y))的(x o y)路径,连接两个部分,这条路径上显然有连接两部分的另一条边,选上它得到另一棵生成树。这条边边权严格小于((x,y))的权,所以原树不可能是MST。得证。
不妨研究一些环,看能不能用这个性质删边。从最简单的三元环入手。
首先,(i<j)间边权可变形为(a_i-id+a_j+jd)。令(A_i=a_i-id,B_i=a_i+id),那么就是(A_i+B_j)。对于三元环((i,j,k)),
- 当(i<j<k)时,((i,j))非严格最大当且仅当(B_jgeq B_k,A_i+B_jgeq A_j+B_k)。注意到(B_jgeq B_k,A_igeq A_j)显然是它的充分条件,我们完全可以只用这个,正确性健在;
- 当(i<k<j)时,((i,j))非严格最大当且仅当(B_jgeq B_k,A_igeq A_k);
- 当(k<i<j)时,((i,j))非严格最大当且仅当(A_i+B_jgeq A_k+B_i,A_igeq A_k)。同(1),充分条件是(B_jgeq B_i,A_igeq A_k)。
把(1,2)放一起研究,可以得出共同点:(B_jgeq B_k,A_igeq A_{ ext{位置在第2的点}})。那么我们想,对于一堆在(i)右边的、(A)值(leq A_i)的点,我们一定可以把它们连向(i)的边互相毙,毙的只剩一个,即那个(B)值最小的。
这看起来很成立,但是似乎仅在严格的时候成立,因为根据上述性质,只有最大边唯一的时候,才可以肆无忌惮地毙掉最大边,否则一般情况下需要保证其他最大边健在。不过在这里的特殊情况,即使非严格也是成立的。证明:若不存在任意一个MST使得它包含任意一条被毙掉的边则已,否则选择一个:设留下来的点是(x)。对于任意一个被毙掉的还在MST里的边((i,y)),若((x,y))不在MST里即可直接换成((x,y)),否则((i,x))一定不在(因为生成树里不可能有环),但是不能直接换成((i,x)),因为它是唯一的,而有那么多被毙掉的边等着换呢。如此,只要证满足((i,y),(x,y))都在MST里的被毙掉的边((i,y))最多只有一个。这是显然的,因为若有两个(y_0,y_1),则((i,x,y_0,y_1))构成四元环。得证。
同理,一堆在(i)左边的、(B)值(leq B_i)的点,我们也可以留下(A)值最小的,其他都毙掉。
不过这样,两个毙法一综合,问题又出现了。。有的点对((i,j)(i<j)),它们之间的边可能会被考虑两次((j)左边和(i)右边),这样就可能是即被删掉,又被留下来的状态。这里有一个巧妙的处理方法:观察(A_igeq A_j),展开得(a_i-idgeq a_j-jd),注意到因为(i<j),所以(-id>-jd),那么充分条件就是(a_igeq a_j)。同理,(B_jgeq B_i)的充分条件是(a_jgeq a_i)。在(a)值互不相同的时候,两者恰好只能选一个,也就是说完全图里每条边恰好被考虑了一遍,就不存在问题了。如果(a)值有相同的呢?只需要再加第二个关键字:位置,这样就一定能比出大小了。
如此,每个点两侧最多各有一条边留下来,边数复杂度(mathrm O(n))。再用BIT xjb随便维护一下建图,然后Kruskal,时间复杂度(mathrm O(nlog n))。
代码也没啥需要说的:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define mp make_pair
#define X first
#define Y second
#define pb push_back
const int inf=0x3f3f3f3f3f3f3f3f;
int lowbit(int x){return x&-x;}
const int N=200000;
int n,d;
int a[N+1];
pair<int,int> p[N+1];
struct bitree{
pair<int,int> mn[N+1];
void init(){
for(int i=1;i<=n;i++)mn[i]=mp(inf,0);
}
void chkmn(int x,pair<int,int> v){
while(x<=n)mn[x]=min(mn[x],v),x+=lowbit(x);
}
int Mn(int x){
pair<int,int> res(inf,0);
while(x)res=min(res,mn[x]),x-=lowbit(x);
return res.Y;
}
}bit,bit_r;
vector<pair<int,int> > eg;
struct ufset{//并查集
int fa[N+1];
void init(){memset(fa,0,sizeof(fa));}
int root(int x){return fa[x]?fa[x]=root(fa[x]):x;}
bool mrg(int x,int y){
x=root(x);y=root(y);
if(x==y)return false;
return fa[x]=y,true;
}
}ufs;
int cst(pair<int,int> x){//边权
if(x.X>x.Y)swap(x.X,x.Y);
return a[x.X]+a[x.Y]+(x.Y-x.X)*d;
}
bool cmp(pair<int,int> x,pair<int,int> y){
return cst(x)<cst(y);
}
int kruskal(){//Kruskal
sort(eg.begin(),eg.end(),cmp);
int ans=0;
// for(int i=0;i<eg.size();i++)printf("%lld %lld
",eg[i].X,eg[i].Y);
for(int i=0;i<eg.size();i++)ans+=ufs.mrg(eg[i].X,eg[i].Y)*cst(eg[i]);
return ans;
}
signed main(){
cin>>n>>d;
for(int i=1;i<=n;i++)scanf("%lld",a+i),p[i]=mp(a[i],i);
sort(p+1,p+n+1);
bit.init();bit_r.init();
for(int i=1;i<=n;i++){//BIT建图
int mn1=bit.Mn(p[i].Y-1),mn2=bit_r.Mn(n-p[i].Y);
if(mn1)eg.pb(mp(p[i].Y,mn1));
if(mn2)eg.pb(mp(p[i].Y,mn2));
bit.chkmn(p[i].Y,mp(p[i].X-p[i].Y*d,p[i].Y));bit_r.chkmn(n-p[i].Y+1,mp(p[i].X+p[i].Y*d,p[i].Y));
}
cout<<kruskal();
return 0;
}
官方题解还给出了另一种分治的方法。就是说,把边分成在左半边内、在右半边内、横跨两半边三类,处理横跨两半边的,另外两种递归下去。考虑如何处理。我们选择左半边(A)值最小的点(x)连所有右半边的点,选择右半边(B)值最小的点(y)连所有左半边的点,这样一来其他所有的横跨两半边的边((x',y'))都被毙掉了,因为((x,y),(x,y'),(x',y))都在,((x',y'))成了环中最大的边。至于严格非严格的问题,xjb再证一下即可。这是在四元环上作文章的。边数复杂度(mathrm O(nlog n)),时间复杂度(mathrm O!left(nlog^2n ight))。
代码没写。应该很好写?
综上所述,下次遇到这样的人类智慧题我还是做不出来。