Wannafly #4 F 线路规划

数据范围252501 劲啊

Q国的监察院是一个神秘的组织。
这个组织掌握了整个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国的天命者——你,请你帮助他们规划出最优的线路。

$n,m \leq 252501$

sol:我们先考虑链上的做法,发现是区间向区间连边然后求 MST,就是一个裸的线段树优化建图 + Kruskal

怎么搞到树上呢?好像不可搞,那就。。。好好听话用 ST 表优化建图吧

我们可以把一个区间拆成 log 层,用 ST 表维护一下,每层看做一个点然后用并查集搞

这样为什么是 log 的呢?我们可以考虑合并的写法

我们设连边的两个区间为$S_1$,$S_2$,我们现在有两个长度为$2^k$的区间$K_1$,$K_2$($K_1$属于$S_1$,$K_2$属于$S_2$)如果$K_1$,$K_2$没连在一起,就连,并递归连它的 log 个子区间,连上了就 return

对于树,我们可以维护一个树上倍增的结构,用 $S_{(i,j)}$ 表示 $i$ 号点和它往上 $2^j$ 层的点组成的集合

合并跟链上一样,最后我们只关心 $j = 0$ 时的连通信息

这样就是一个优秀的一个 log 的做法了

 

emmmmmmmmmmm

“题是好题,但是要卡常”

wzj52501怎么跑的那么快呀QQQAQ

不管了不管了,常以后再卡吧

#include<bits/stdc++.h>
#define LL long long
using namespace std;
inline int read()
{
    int x = 0,f = 1;char ch = getchar();
    for(;!isdigit(ch);ch = getchar())if(ch == '-')f = -f;
    for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0';
    return x * f;
}
const int maxn = 252501;
int n,m;
int fa[maxn][20],pos[maxn][20];
struct data
{
    int x,y,k,w;
    bool operator < (const data &b)const{return w < b.w;}
}qs[maxn];
int ufs[4545180],size[maxn],dfn;
LL val[maxn];
inline int find(int x){return x == ufs[x] ? x : ufs[x] = find(ufs[x]);}
void merge(int a,int b,int k,int v)
{
    int fu = find(pos[a][k]),fv = find(pos[b][k]);
    if(fu == fv)return;
    ufs[fv] = fu;
    if(!k){size[fu] += size[fv];val[fu] += val[fv] + v;return;}
    merge(a, b,k - 1,v);
    merge(fa[a][k-1],fa[b][k-1],k - 1,v);
}
int main()
{
    n = read(),m = read();
    for(int i=2;i<=n;i++)
    {
        fa[i][0] = read();
        for(int j=1;j<=17;j++)fa[i][j] = fa[fa[i][j - 1]][j - 1];
    }
    for(int i=1;i<=m;i++)
        qs[i].x = read(),qs[i].y = read(),qs[i].k = read(),qs[i].w = read();
    sort(qs + 1,qs + m + 1);
    for(int i=1;i<=n;i++)pos[i][0] = ++dfn,size[dfn] = 1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=17;j++)pos[i][j] = ++dfn;
    for(int i=1;i<=dfn;i++)ufs[i] = i;
    for(int i=1;i<=m;i++)
    {
        int u = qs[i].x,v = qs[i].y;
        for(int j=17;~j;j--)
            if(qs[i].k >> j & 1)
            {
                merge(u,v,j,qs[i].w);
                u = fa[u][j],v = fa[v][j];
            }
    }
    int ans1 = 0;LL ans2 = (1LL << 60);
    for(int i=1;i<=n;i++)
    {
        int fu = find(i);
        if(ans1 < size[fu])ans1 = size[fu],ans2 = val[fu];
        else if(ans1 == size[fu] && ans2 > val[fu]) ans2 = val[fu];
    }
    printf("%d %lld",ans1,ans2);
}
View Code

哪位大手子教教我怎么卡常呀QQQAQ

posted @ 2018-10-23 19:45  探险家Mr.H  阅读(224)  评论(0编辑  收藏  举报