【Wannafly挑战赛4】F 线路规划 倍增+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;
}
时间: 2024-10-07 20:33:47

【Wannafly挑战赛4】F 线路规划 倍增+Kruskal+归并的相关文章

Wannafly挑战赛14 F.细胞

题解:NTT.二项式定理 再逆FFT求出系数ans[i],本题即可解了 另:采用FFT的话,复数既不方便,误差也很大. 从FFT到NTT: 由费马小定理可知 gp-1%p=1    (p为质数) 所以利用这个性质来对应单位复数根乘方的周期性,即 代码: #include<iostream> using namespace std; typedef long long ll; const ll Mod=998244353; const ll G=3; ll kpow(ll a,ll k) { l

Wannafly挑战赛18

Wannafly挑战赛18 A. 序列 先考虑暴力,相邻两个树之间乘上给定的三种数,递推出下一个位置填什么,然后再check一下,最后一位是否为1即可.这样时间显然不行,但是给我们一种思路,就是中间的转换关系,确定唯一一个序列.现在的目标是让最后一位出现1,可以如果不管1,由-2和0.5取凑出1需要两个-2和两个0.5.那所有的转换中,就只要保证有若干组(-2,-2,0.5,0.5)存在,其他地方为1即可.具体公式见代码 #include <bits/stdc++.h> #define rep

Wannafly挑战赛19

Wannafly挑战赛19 A. 队列Q 需要支持把一个元素移到队首,把一个元素移到队尾,移到队首就直接放到队首前面那个位置,原位置标为0,队尾同理. #include <bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;++i) typedef long long ll; const int N = 30000200; using namespace std; int n,m; int q[N],hd,x,ed,P[N]; char

Wannafly挑战赛25游记

Wannafly挑战赛25游记 A - 因子 题目大意: 令\(x=n!(n\le10^{12})\),给定一大于\(1\)的正整数\(p(p\le10000)\)求一个\(k\)使得\(p^k|x\)并且\(p^{k+1}\not|x\)的因子. 思路: 枚举\(p\)的每一个质因数\(q\),求出它在\(n!\)出现次数\(/p\)中出现次数,取\(\min\)即可.对于一个质因数\(q\),在\(n!\)中出现的次数等于\(\sum_{i=1}^{\inf}\frac n{q^i}\).

百度地图开发之poi检索,线路规划

  官方文档 http://lbsyun.baidu.com/index.php?title=androidsdk/guide/key 先去官方文档申请秘钥下载压缩文件等操作,参考 百度地图的秘钥申请与sdk下载,显示地图(1) 公交,步行,驾车路线查询,附近POI查询 参考文档导入所需jar包和so文件 标注点,覆盖物的实现见官方文档  poi检索:1.首先获取地图,创建poi检索实例: private void initView() { //输入框 etPoi = (EditText) in

百度地图绘制实时路线以及最短线路规划

如何使用百度地图绘制实时路线以及最短线路规划 最近在做百度地图的实时路线绘制,发现一些问题,比如由于定位漂移带来的路线绘制偏差,还有由于定位漂移,导致人未走动时,也会绘制路线等.百度鹰眼的线路纠偏个人感觉很一般啊.而且有限漂移了两百米的点他也没有纠正过来.所以最后还是决定自己写一个纠偏吧.而且百度地图官方的dome和示例代码真的很示例啊.然人摸不着头脑.ok进入正题,思路是这样的,因为实时绘制线路都是在室外,所以只采用gps定位,不采用无线网络定位.这样漂移一两百米的点基本不会出现.第二当人在等

Wannafly挑战赛3

Wannafly挑战赛3 A    珂朵莉 B    遇见 水题 #include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for (int i=a; i<=b; ++i) #define per(i,b,a) for (int i=b; i>=a; --i) #define mes(a

牛客网 Wannafly挑战赛8 C-小C打比赛 (状压DP)

小C现在要参加一场wannafly挑战赛,一场挑战赛一共有n道题,一共有m分钟. 对于第i道题,小C解决它需要恰好j分钟的概率是pi,j. 小C每次会选择某一道没做完的题,然后把它解决(不能中途放弃),之后再决策下一道要做的题是哪道. 求小C在最优策略下,期望能做出几道题. 输入描述: 第一行两个正整数n,m接下来一共n行,每行有m个小数,第i行的第j个小数表示p i,j (这里假设不存在0分钟A题的dalao). 输出描述: 输出一个小数,表示期望能做出几道题,保留小数点后五位. 示例1 输入

Wannafly挑战赛11 D 白兔的字符串 Hash

Wannafly挑战赛11 D   白兔的字符串 白兔有一个字符串T.白云有若干个字符串S1,S2..Sn. 白兔想知道,对于白云的每一个字符串,它有多少个子串是和T循环同构的. 提示:对于一个字符串a,每次把a的第一个字符移动到最后一个,如果操作若干次后能够得到字符串b,则a和b循环同构. 所有字符都是小写英文字母 输入描述: 第一行一个字符串T(|T|<=10^6)第二行一个正整数n (n<=1000)接下来n行为S1~Sn (|S1|+|S2|+…+|Sn|<=10^7),max(