线路规划
思路
简单总结一下。
让我们求一个最小生成树。但是其中如果我们暴力建边一定会T。
我们考虑用倍增优化。
对于每一组线路方案,我们按照二进制将其拆开。
越靠近端点的区间越大。也可以理解为对同一组方案,其中深度越大的点所在的拆分的区间越大。
接下来,我们将端点之间的连边转换为了区间之间的连边,因为区间之间最多只有logn个,则建边不会T掉。
我们将题目给出的边整理到新的数组中,我们只需要对相同区间长度的部分,求最小生成树。
首先可以确定的是,我们一定是从深度最深的边,但这样直接加的可能出问题。
例如,(3,4,4,10)和(3,4,2,1)两组边,我们先看(3,4,4,10)就会挤占掉(3,4,2,1)的机会,但是显然(3,4,2,1)更短。
所以实际上我们对 2p 值相等的边跑kruskal不是为把最后的树连出来,而是把当前这个 2p 下完全没有用(已经被更好的边联通)的边扔掉,没有扔掉的边则加入下一层的kruskal,即我们对每一个 2p 都单独建立生成树——所以对于当前p之下从edge里面拿出来的每一条边,我们判断他的两个下端点在当前p下有没有联通(下端点联通也有意味着整个区间都可以用这样长的边联通,不管是间接还是直接),联通的话这条边直接舍弃,没联通的连上。
总的来说就是,虽然对于当前区间长度,我们选择的使其联通的最小的边权,但可能并不是最优解,当区间更小时,可能出现更优解。因此我们需要将本次的选择传递下去,也就是将这个大区间拆开为两个小区间,传递到下一层中,再进行比较。
但是,我们不能直接加到下一层的数组中,因为我们需要他们按照权值从小到大排序好,如果每次加入我们都重新排序的话,时间复杂度就过不去了。但是我们仔细观察后,会发现,因为我们对于每个区间长度相同而言的一堆区间而言,本来就是有序的。则向下传递到下一个区间长度的时候,我们用另外一个数组储存下传的区间,其也是有序的。那两个有序的区间,我们可以直接O(n)的合并(当然也不用真的合并)。
至此题目结束。
可以配合官方题解的图片再理解一下。
看看代码吧。
Ac_code
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 260000;
int n,m,ans1;
LL ans2;
int h[N],e[N],ne[N],idx;
int fa[N][20],p[N][20],dep[N],sz[N];
LL sum[N];
struct Edge
{
int a,b,w;
};
struct Node
{
int a,b,k,w;
bool operator<(const Node& W) const
{
return w<W.w;
}
}query[N];
vector<Edge> s1[20],s2[20];
void add(int a,int b)
{
e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
int find(int x,int y)
{
if(p[x][y]!=x) p[x][y] = find(p[x][y],y);
return p[x][y];
}
void bfs()
{
dep[1] = 1;
queue<int> q;
q.push(1);
while(q.size())
{
auto t = q.front();
q.pop();
for(int i=h[t];~i;i=ne[i])
{
int j = e[i];
q.push(j);
dep[j] = dep[t] + 1;
fa[j][0] = t;
for(int k=1;k<20;k++) fa[j][k] = fa[fa[j][k-1]][k-1];
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) h[i] = -1,sz[i] = 1;
for(int i=0;(1<<i)<=n;i++)
for(int j=1;j<=n;j++)
p[j][i] = j;
for(int i=2;i<=n;i++)
{
int x;scanf("%d",&x);
add(x,i);
}
bfs();
for(int i=1;i<=m;i++)
{
int a,b,k,w;scanf("%d%d%d%d",&a,&b,&k,&w);
query[i] = {a,b,k,w};
}
sort(query+1,query+1+m);
for(int i=1;i<=m;i++)
{
auto [a,b,k,w] = query[i];
for(int j=19;j>=0;j--)
if(k>=(1<<j))
{
k-=(1<<j);
s1[j].push_back({a,b,w});
a = fa[a][j];
b = fa[b][j];
}
}
for(int i=19;i>=0;i--)
{
vector<Edge>::iterator it;
for(auto i1=s1[i].begin(),i2=s2[i].begin();i1!=s1[i].end()||i2!=s2[i].end();)
{
if(i1!=s1[i].end()&&(i2==s2[i].end()||(*i1).w<(*i2).w)) it = i1,i1++;
else it = i2,i2++;
int a = (*it).a,b = (*it).b,w = (*it).w;
int pa = find(a,i),pb = find(b,i);
if(pa!=pb)
{
if(i==0) sz[pb] += sz[pa],sum[pb] += sum[pa] + w;
p[pa][i] = pb;
if(i) s2[i-1].push_back({a,b,w}),s2[i-1].push_back({fa[a][i-1],fa[b][i-1],w});
}
}
}
for(int i=1;i<=n;i++)
if(find(i,0)==i)
{
if(sz[i]>ans1) ans1 = sz[i],ans2 = sum[i];
else if(sz[i]==ans1) ans2 = min(ans2,sum[i]);
}
printf("%d %lld",ans1,ans2);
return 0;
}

浙公网安备 33010602011771号