Wannafly挑战赛4F 线路规划 倍增+Kruskal+归并

Posted CQzhangyu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Wannafly挑战赛4F 线路规划 倍增+Kruskal+归并相关的知识,希望对你有一定的参考价值。

【Wannafly挑战赛4】F 线路规划

题目描述

Q国的监察院是一个神秘的组织。
这个组织掌握了整个帝国的地下力量,监察着Q国的每一个人。
监察院一共有N个成员,每一个成员都有且仅有1个直接上司,而他只听从其上直接司的命令。其中1号成员是监察院的院长,这个庞然大物的主人。
由于时代的进步,监察院议会决定升级组织的旧式通信器,安装最新的反侦测通信器。
他们拿出了M组线路方案,其中第i组线路方案可以用一个四元组(x[i]、y[i]、k[i]、w[i])描述,表示第x[i]号成员可以安装与y[i]号成员的直接通信线路,费用为w[i];x[i]号成员的上司可以安装与y[i]号成员的上司的直接通信线路,费用为w[i];x[i]号成员的上司的上司可以安装与y[i]号成员的上司的上司的直接通信线路,费用为w[i]; …… ;x[i]号成员的k[i] - 1级上司可以安装与y[i]号成员的k[i] - 1级上司的直接通信线路,费用为w[i]。(这k[i]条线路的费用独立计算)
如果一个集合内部的成员两两之间都可以通过直接或间接的通信线路进行通信,那么这个集合的所有成员可以成立一个特别行动组。
监察院想成立一个成员最多的特别行动组,同时他们想让安装线路的费用之和最小,
所以他们找到了Q国的天命者——你,请你帮助他们规划出最优的线路。

输入描述:

第一行为2个正整数N、M。
第二行为N - 1个正整数L[i],第i个正整数表示第i+1个成员的直接上司L[i]。
接下来M行每行四个正整数x[i],y[i],k[i],w[i]。

输出描述:

仅一行,为特别行动组成员人数的最大值和在此前提下安装线路的最小费用之和。
示例1

输入

5 3
1 1 2 2
5 4 3 10
1 3 1 5
2 4 2 3

输出

5 21

说明

设(u、v、w)表示一条u到v,费用为w的线路。
则一共有(5、4、10)、(2、2、10)、(1、1、10)、(1、3、5)、(2、4、3)、(1、2、3)共6条线路。
选择第1、4、5、6条线路,可以成立特别行动组{1、2、3、4、5},费用之和为21

备注:

对于100%的数据:

1 ≤ N、M ≤ 252501

1≤x[i],y[i],k[i]≤N,1≤L[i]≤i - 1,保证x[i]、y[i]号成员均至少有k[i]个上司,1≤w[i]≤109。

题解:看题意是想让你求一个类似最小生成树的东西,但是直接求肯定不行,我们考虑用什么方法来优化求最小生成树的过程。

最基本,也是最重要的第一思路就是倍增。我们用倍增把k拆成log段,每段的长度都形如$2^j$。然后我们从大到小考虑所有的j,将所有形如$2^j$的段放到一起跑最小生成树。然后将树边pushdown下去,继续做下一层,最后在第0层计算费用,时间复杂度$O(nlog^2_n)$。

然而n=250000,O(nlog2n)会TLE。我们考虑能否干掉一个log。我们想到哪到题?NOIP2016蚯蚓!我们可以先将所有边排序,再分段,这样每一段一开始就都是有序的了,并且pushdown下来的边也都是有序的,我们将这两种边分开存,最后归并一下就又是有序的了,时间复杂度就变成$O(nlogn)$了。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=260000;
int n,m,ans1;
ll ans2;
int fa[20][maxn],f[20][maxn],Log[maxn],dep[maxn],siz[maxn];
ll sum[maxn];
struct edge
{
    int a,b,w;
    edge() {}
    edge(int a1,int a2,int a3) {a=a1,b=a2,w=a3;}
};
struct node
{
    int a,b,k,w;
}p[maxn];
vector<edge> s1[20],s2[20];
vector<edge>::iterator i1,i2,it;
bool cmp2(const node &a,const node &b)
{
    return a.w<b.w;
}
int find(int x,int y)
{
    return (f[y][x]==x)?x:(f[y][x]=find(f[y][x],y));
}
inline void updata(int a,int b,int x,int w)
{
    if(find(a,x)!=find(b,x))    f[x][f[x][a]]=f[x][b];
}
inline int rd()
{
    int ret=0,f=1;  char gc=getchar();
    while(gc<\'0\'||gc>\'9\') {if(gc==\'-\')    f=-f;   gc=getchar();}
    while(gc>=\'0\'&&gc<=\'9\')   ret=ret*10+(gc^\'0\'),gc=getchar();
    return ret*f;
}
int main()
{
    n=rd(),m=rd();
    register int i,j,a,b,k,w;
    dep[1]=1;
    for(i=2;i<=n;i++)    fa[0][i]=rd(),dep[i]=dep[fa[0][i]]+1,Log[i]=Log[i>>1]+1;
    for(i=1;i<=n;i++)    f[0][i]=i;
    for(j=1;(1<<j)<=n;j++) for(i=1;i<=n;i++)    fa[j][i]=fa[j-1][fa[j-1][i]],f[j][i]=i;
    for(i=1;i<=m;i++)    p[i].a=rd(),p[i].b=rd(),p[i].k=rd(),p[i].w=rd();
    sort(p+1,p+m+1,cmp2);
    for(i=1;i<=m;i++)
    {
        a=p[i].a,b=p[i].b,k=p[i].k,w=p[i].w;
        for(j=Log[k];j>=0;j--)   if(k>=(1<<j))
            k-=(1<<j),s1[j].push_back(edge(a,b,w)),a=fa[j][a],b=fa[j][b];
    }
    for(i=1;i<=n;i++)    siz[i]=1;
    for(j=Log[n];j>=0;j--)
    {
        for(i1=s1[j].begin(),i2=s2[j].begin();i1!=s1[j].end()||i2!=s2[j].end();)
        {
            if(i1!=s1[j].end()&&(i2==s2[j].end()||(*i1).w<(*i2).w))  it=i1,i1++;
            else    it=i2,i2++;
            a=(*it).a,b=(*it).b,w=(*it).w;
            if(find(a,j)!=find(b,j))
            {
                if(j==0)    siz[f[j][b]]+=siz[f[j][a]],sum[f[j][b]]+=sum[f[j][a]]+w;
                f[j][f[j][a]]=f[j][b];
                if(j)   s2[j-1].push_back(edge(a,b,w)),s2[j-1].push_back(edge(fa[j-1][a],fa[j-1][b],w));
            }
        }
    }
    for(i=1;i<=n;i++)    if(find(i,0)==i)
    {
        if(siz[i]>ans1)  ans1=siz[i],ans2=sum[i];
        if(siz[i]==ans1)    ans2=min(ans2,sum[i]);
    }
    printf("%d %lld",ans1,ans2);
    return 0;
}

以上是关于Wannafly挑战赛4F 线路规划 倍增+Kruskal+归并的主要内容,如果未能解决你的问题,请参考以下文章

Wannafly挑战赛21 E 未来城市规划

Wannafly挑战赛27

Wannafly挑战赛3

Wannafly挑战赛21A

Wannafly挑战赛19

Wannafly挑战赛25游记