P4616 [COCI2017-2018#5] Pictionary

P4616 [COCI2017-2018#5] Pictionary

分析

啊,也不算很麻烦,我们主要展示思路过程。

首先,看到q次询问中,每次询问使得ab的联通的最早时间。那我们自然想到,可以直接求abgcd,然后就可以得到一定能将其连接起来的日子了。

然后发现,这没什么用,因为在这之前的某天可能已经将两个点分别所在的连通块连起来过了。

那,我们就转换思路,首先,我们能想到的是。

对于每一天i而言,如果想将GCD等于m-i+1的城市全部连接起来,那我们只需要将城市m-i+1与其倍数的城市全部连接起来,这样就可以办到了,不难证明,时间复杂度是\(O(NlogN)\)的。想想为什么这样的图与原来那样建图等价(提示:我们只考虑联通性),将边权设为建立的那天

接下来,我们转化一下题目,就是求任意两点之间,使其联通的所有路径中的最大边权的最小值。

这就是个经典问题了,解决方法很多,这里提一下,但不会都实现。其中包括Kruskal重构树建立最小生成树,求两点之间路径的最大值

可以直接去看看P1967 [NOIP2013 提高组] 货车运输

但不论第一种还是第二种,我们都要用到Kruskal算法,将边权从小到大排序,再建树。

而正好,我们在遍历天数的时候,就是边权最小到大建边的,因此直接按顺序枚举跑Kruskal就可以了。

对于第一种,这就是一个板子,建完树求LCA就是答案了。代码会稍微短一些。可以尝试一下。

我们这里说的是第二个方法。

先建立最小生成树,接着求两点之间路径的最大值。解决方案,也很多了。

主要体现在你用什么办法维护最大值。

如果你用倍增法,直接多维护一个数组维护路径最值即可。

如果你用树剖,就得用一下线段树了。

我们总结一下:

这题最难的点在于,建图的时候,如果把原图转化。我们根据的是只需要连通性,因此直接从m-i+1城市直接连向其倍数,就能更跟原图创造一样的连通性。

最后就是经典问题了。

Ac_code

#include<bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10,M = N<<1;
struct Node
{
    int l,r,mx;
}tr[N<<2];
int h[N],ne[M],e[M],w[M],idx;
int sz[N],son[N],fa[N],dep[N],val[N];
int top[N],nw[N],id[N],ts;
int p[N];
int n,m,q;

void add(int a,int b,int c)
{
    e[idx] = b,ne[idx] = h[a],w[idx] = c,h[a] = idx++;
}

int find(int x)
{
    if(p[x]!=x) p[x] = find(p[x]);
    return p[x];
}

void dfs1(int u,int pa,int depth)
{
    sz[u] = 1,dep[u] = depth;
    // cout<<u<<endl;
    for(int i=h[u];~i;i=ne[i])
    {
        int j = e[i];
        if(j==pa) continue;
        fa[j] = u;
        val[j] = w[i];
        dfs1(j,u,depth+1);
        sz[u] += sz[j];
        if(sz[j]>sz[son[u]]) son[u] = j;
    }
}

void dfs2(int u,int tp)
{
    top[u] = tp,id[u] = ++ts,nw[ts] = val[u];
    if(!son[u]) return ;
    dfs2(son[u],tp);
    for(int i=h[u];~i;i=ne[i])
    {
        int j = e[i];
        if(j==fa[u]||j==son[u]) continue;
        dfs2(j,j);
    }
}

void pushup(int u)
{
    tr[u].mx = max(tr[u<<1].mx,tr[u<<1|1].mx);
}

void build(int u,int l,int r)
{
    if(l==r) 
    {
        tr[u] = {l,r,nw[l]};
        return ;
    }
    tr[u] = {l,r};
    int mid = l + r >> 1;
    build(u<<1,l,mid),build(u<<1|1,mid+1,r);   
    pushup(u);
    return ;
}

int query(int u,int l,int r)
{
    if(l<=tr[u].l&&tr[u].r<=r) return tr[u].mx;
    int res = 0;
    int mid = tr[u].l + tr[u].r >> 1;
    if(l<=mid) res = max(res,query(u<<1,l,r));
    if(r>mid) res = max(res,query(u<<1|1,l,r));
    return res;
}

int main()
{
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n;i++) h[i] = -1,p[i] = i;
    for(int i=1;i<=m;i++)
    {
        int pa = find(m-i+1);
        for(int j=2;j*(m-i+1)<=n;j++)
        {
            int pb = find(j*(m-i+1));
            if(pa!=pb)
            {
                add(m-i+1,j*(m-i+1),i),add(j*(m-i+1),m-i+1,i);
                p[pb] = pa;
            }
        }
    }
    dfs1(1,-1,1);
    dfs2(1,1);
    build(1,1,n);
    while(q--)
    {
        int u,v;cin>>u>>v;
        int res = 0;
        while(top[u]!=top[v])
        {
            if(dep[top[u]]<dep[top[v]]) swap(u,v);
            res = max(res,query(1,id[top[u]],id[u]));
            u = fa[top[u]];
        }
        if(dep[u]<dep[v]) swap(u,v);
        if(u!=v) res = max(res,query(1,id[v]+1,id[u]));
        cout<<res<<endl;
    }
    return 0;
}
posted @ 2022-04-25 21:55  艾特玖  阅读(59)  评论(0)    收藏  举报