链接:https://ac.nowcoder.com/acm/contest/27836/K
来源:牛客网
题目描述
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 ≤ 2525011≤x[i],y[i],k[i]≤N,1≤L[i]≤i - 1,保证x[i]、y[i]号成员均至少有k[i]个上司,1≤w[i]≤109
。
分析:
如果没有 K,直接朴素的krukal就行了,边权最短,点数最多。
但是这题有 K,可以用倍增的思想,先把所有点存到vector<node> v[N] 数组里,表示将所有 2^j 个祖先都变成相关关系的的节点。
然后从大到小 枚举 j,先将所有点排序,将所有边 用并查集去掉那些权值较大且加入集合没有意义的边。并将它们拆成 表示所有 2^(j-1) 个祖先都变成相关关系的节点。
最后只剩表示 2^0 也就是表示 自己的另两个点有相关关系的边。将它们用kruskal从小边权开始遍历,找到点数最多,边权最短的集合。
const int N=3e5+10; int f[N][30],fa[30][N],root[N]; long long val[N]; struct unio { int x,y,k,w; }a[N]; int finds(int p,int x) { return x==fa[p][x]?x:fa[p][x]=finds(p,fa[p][x]); } vector<unio> v[N]; bool cmp(unio p,unio q) { return p.w<q.w; } void solve() { int n,m; cin>>n>>m; for(int i=2;i<=n;i++) cin>>f[i][0]; for(int i=1;i<=n;i++){ for(int j=1;j<20;j++){ f[i][j]=f[f[i][j-1]][j-1]; } } for(int i=1;i<=m;i++){ scanf("%d%d%d%d",&a[i].x,&a[i].y,&a[i].k,&a[i].w); for(int j=19;j>=0;j--) { if((1<<j)<=a[i].k) { unio tmp; tmp.x=a[i].x,tmp.y=a[i].y,tmp.k=j,tmp.w=a[i].w; v[j].push_back(tmp); a[i].x=f[a[i].x][j]; a[i].y=f[a[i].y][j]; a[i].k-=(1<<j); if(!a[i].k) break; } } } for(int i=19;i>0;i--){ if(!v[i].size()) continue; sort(v[i].begin(),v[i].end(),cmp); for(int j=1;j<=n;j++) fa[i][j]=j; for(int j=0;j<v[i].size();j++){ int x=finds(i,v[i][j].x); int y=finds(i,v[i][j].y); //cout<<v[i][j].x<<" "<<v[i][j].y<<" "<<x<<" "<<y<<endl; if(x!=y){ fa[i][x]=y; unio tmp; tmp.x=v[i][j].x,tmp.y=v[i][j].y,tmp.k=v[i][j].k-1,tmp.w=v[i][j].w; v[i-1].push_back(tmp); tmp.x=f[v[i][j].x][v[i][j].k-1],tmp.y=f[v[i][j].y][v[i][j].k-1]; v[i-1].push_back(tmp); } } } long long ans=1e18,s=0; sort(v[0].begin(),v[0].end(),cmp); for(int j=1;j<=n;j++){ fa[0][j]=j; root[j]=1; } for(int i=0;i<v[0].size();i++) { int x=finds(0,v[0][i].x); int y=finds(0,v[0][i].y); //cout<<v[0][i].x<<" "<<v[0][i].y<<" "<<v[0][i].w<<" "<<x<<" "<<y<<endl; if(x!=y) { fa[0][x]=y; root[y]+=root[x]; val[y]+=v[0][i].w+val[x]; if(root[y]>s) { s=root[y]; ans=val[y]; } else if(root[y]==s) ans=min(ans,val[y]); } } if(s==0) ans=0; cout<<s<<" "<<ans<<endl; }
标签:10,上司,int,kruskal,通信线路,线路,删去,倍增,成员 From: https://www.cnblogs.com/er007/p/16660248.html