AtCoder keyence2019_e - Connecting Cities
洛谷的AtC接口貌似坏掉了,水不了这道紫题了,但是题解还是可以水的(大雾
洛谷题目页面传送门 & AtC题目页面传送门
有\(n\)个点,第\(i\)个点的权值为\(a_i\)。给定常数\(d\),在点\(i,j\)之间连边的代价是\(|i-j|d+a_i+a_j\)。求将它们连成一棵树的最小代价。
\(n\in\left[1,10^5\right]\)。
首先不难发现,原题相当于,求完全图的MST的权,其中点\(i,j\)之间的边权为\(|i-j|d+a_i+a_j\)。如果直接Kruskal的话,建图就要爆炸,边数是\(\mathrm O\!\left(n^2\right)\)的。考虑用某些MST的奇技淫巧。
我们的目的是,尽可能删掉一些边,留下尽量少的边,使得MST的权不变。
注意到这样一条经典的性质:MST不可能包含一个环上所有的最大边。证明很简单:反证法。若包含,则随意去掉一个环上的最大边\((x,y)\),此时整棵树断成两部分,\(x,y\)在不同的部分。由于是个环,那么环上一定有一条反向的不包含 \((x,y)\)的\(x\to 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_j\geq B_k,A_i+B_j\geq A_j+B_k\)。注意到\(B_j\geq B_k,A_i\geq A_j\)显然是它的充分条件,我们完全可以只用这个,正确性健在;
- 当\(i<k<j\)时,\((i,j)\)非严格最大当且仅当\(B_j\geq B_k,A_i\geq A_k\);
- 当\(k<i<j\)时,\((i,j)\)非严格最大当且仅当\(A_i+B_j\geq A_k+B_i,A_i\geq A_k\)。同\(1\),充分条件是\(B_j\geq B_i,A_i\geq A_k\)。
把\(1,2\)放一起研究,可以得出共同点:\(B_j\geq B_k,A_i\geq A_{\text{位置在第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_i\geq A_j\),展开得\(a_i-id\geq a_j-jd\),注意到因为\(i<j\),所以\(-id>-jd\),那么充分条件就是\(a_i\geq a_j\)。同理,\(B_j\geq B_i\)的充分条件是\(a_j\geq a_i\)。在\(a\)值互不相同的时候,两者恰好只能选一个,也就是说完全图里每条边恰好被考虑了一遍,就不存在问题了。如果\(a\)值有相同的呢?只需要再加第二个关键字:位置,这样就一定能比出大小了。
如此,每个点两侧最多各有一条边留下来,边数复杂度\(\mathrm O(n)\)。再用BIT xjb随便维护一下建图,然后Kruskal,时间复杂度\(\mathrm O(n\log 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\n",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(n\log n)\),时间复杂度\(\mathrm O\!\left(n\log^2n\right)\)。
代码没写。应该很好写?
综上所述,下次遇到这样的人类智慧题我还是做不出来。