牛客练习赛38 F 出题人的无向图(level 3)(启发式合并优化+离线+线段树/堆维护最大值)

本文探讨了一种在图论基础上的复杂算法优化方案,通过离线询问和启发式合并策略,实现了对连通块权值的最大值计算。采用线段树和多集合维护更新过程,确保了算法效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接

题意:

给你一个n个点,m条边的无向图,每一个点有两个属性ai,bi<=INT_MAX

有q个询问,每一个询问,一个边界v,和k个点c1..ck

对于每一次询问,进行操作(此操作只作用于本次询问)

先将ai>v的点全部删除,包括连接这些点的边

在把剩下的图中编号为c1..ck这些点各自对应的连通块删除

再对最后剩下的图中的连通块定义其权值:一个连通块的权值是

连通块中的不同的属性b数量(这个连通块中存在此属性b,且出现次数为kk的倍数)

每个询问输出此时所有联通块的权值的最大值

 

解析:

这道题原本的题面对于删除k个点对应得连通块这一个操作和删除ai>v的点的操作先后性根本没有讲...

导致我一开始以为,先删那k个连通块再删ai>v的点。...后来看了别人的代码,发现顺序刚好相反.....

做法的大致思路就是

边先保存起来,每一个点都看作一个连通块

然后离线询问,将询问按照v值从小到大排序

一开始是一个空图,对于每一个询问,我们把ai<=v的点全部加入到图中

,同时把这些点连接的边也一并加入到图中。

这里的核心步骤就是加点。

例如当加入新的点x,我们要x连接的边(同时这个边的另外一个节点已经在图中)都加入到图中

,当加入边的过程中,我们都同时要进行合并连通块的操作(启发式合并,size小的并入siz大的,时间优化)——

把siz小的连通块的各个点的值,加入到siz大的连通块的mp中,同时不断更新siz大的连通块的权值

因为边的加入就会产生连通块。

并且在这些步骤的同时我们也还要一直更新连通块的权值(旧的权值删除,新的权值加入),并且维护这些里面的最大值

ps:对于每一个询问,我们维护的应该是ai<=这个询问的v的所有点组成的各个连通块的权值的最大值

(千万不要漏点,我一开始用边插入的时候,就是因为这个地方错的)

这里我用的是线段树来维护的,叶子节点表示的是编号为x的连通块的权值。

我看其他大佬的代码也有用堆来维护的,用的是multiset,因为这里的堆需要用到定值删除的操作(给定一个值,在堆中删除他)

用堆维护很简单,更新的时候就把原来的那个值从堆中删除,然后再把新的值插入进去。

删除k个连通块时,也只需要把这k个连通块对应的值从堆中删除,求最大值,然后再把删除的值插入进去就可以了。

用线段树的话,我是先将这k个点,按照他们连通块的编号排序,然后按照这些需要删除的连通块的序号把

[1,n]分成一段段区间,然后区间查询最大值,然后再取最大值的最大值。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
using namespace std;
#define mk make_pair
typedef long long ll;
typedef pair<int,int> PII;
const int N = 2e5+10;
const int M = 5e5+10;

typedef struct point
{
    int first;
    int second;
    int id;
    friend bool operator<(point a,point b)
    {
        return a.first<b.first;
    }

}point;

point node[N];

int n,m,kk;

int a[N],sz[N];

int vis[N];
vector<int> edge[N];

int fa[N];

int k[N];

int Btree[N*4];

map<int,int> mp[N];

int ans[M],val[N];

typedef struct query
{
    int v;
    int l,r;
    int id;
}query;

query mq[M];
//set<int> father;

int findfa(int x)
{
    if(x==fa[x]) return x;
    int nex=fa[x];
    fa[x]=findfa(nex);
    return fa[x];
}

bool cmp(query a,query b)
{
    return a.v<b.v;
}

bool cmpk(int a,int b)
{
    return findfa(a)<findfa(b);
}

void build(int l,int r,int root)  //l,r表示他们在stu中的下标,root表示他们在线段树中的坐标
{
    if(l>r)return;
    if(l==r)
    {
        Btree[root]=0;
        return;
    }

    int mid=(l+r)/2;

    build(l,mid,root*2);
    build(mid+1,r,root*2+1);

    Btree[root]=max(Btree[root*2],Btree[root*2+1]);
}


void updateone(int root,int l,int r,int index,int val)
{
    if(l>r)return;
    if(l==r)
    {
        if(l==index)
            Btree[root]+=val;
        return;
    }

    int mid=(l+r)/2;
    if(index<=mid)
        updateone(root*2,l,mid,index,val);
    else
        updateone(root*2+1,mid+1,r,index,val);

    Btree[root]=max(Btree[2*root],Btree[2*root+1]);
}

int query_max(int root,int s1,int e1,int s2,int e2)
{
    if(e1<s2||s1>e2)
        return 0;
    if(s1>e1)return 0;
    if(s1>=s2&&e1<=e2)
    {
        return Btree[root];
    }

    //pushDown(root);
    int mid=(s1+e1)/2;
    int lm,rm;
    lm=rm=0;
    if(s2<=mid)
        lm=query_max(root*2,s1,mid,s2,e2);
    if(mid+1<=e2)
        rm=query_max(root*2+1,mid+1,e1,s2,e2);
    return max(lm,rm);
}

void combine(int u,int v)
{
    if(sz[u]<sz[v]) swap(u,v);  //启发式合并
    sz[u]+=sz[v];
    sz[v]=0;
    fa[v]=u;
    int past=val[u];
    for(auto s:mp[v])
    {
        int su=0;
        if(mp[u].count(s.first))
        {
            su=mp[u][s.first];
            if(!su) val[u]-=1;
        }

        su=(su+s.second)%kk;
        if(!su) val[u]+=1;
        mp[u][s.first]=su;

    }
    updateone(1,1,n,u,val[u]-past);
    updateone(1,1,n,v,-val[v]);
    val[v]=0;


}

void cal(int x)
{
    vis[x]=1;
    updateone(1,1,n,x,val[x]);
    for(auto s:edge[x])
    {
        if(vis[s]){
            int fx=findfa(x);
            int fy=findfa(s);
            if(fx!=fy) combine(fx,fy);
        }
    }
}




int main()
{
    scanf("%d%d%d",&n,&m,&kk);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&node[i].first);
        fa[i]=i;
        node[i].id=i;
        vis[i]=0;
        sz[i]=1;
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&node[i].second);
        mp[i][node[i].second]=1%kk;
        val[i]=(kk==1?1:0);
    }


    sort(node+1,node+1+n);
    for(int i=1;i<=n;i++) a[i]=node[i].first;
    for(int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        edge[u].push_back(v);
        edge[v].push_back(u);
    }
    int sz=n;

    build(1,sz,1);
    int q,cnt;
    cnt=0;
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
    {
        int tmp;
        scanf("%d%d",&mq[i].v,&tmp);
        mq[i].l=cnt;
        mq[i].r=cnt+tmp;
        mq[i].id=i;
        while(cnt<mq[i].r)
        {
            scanf("%d",&k[cnt]);

            cnt++;
        }
    }
    sort(mq+1,mq+1+q,cmp);
    int pre=0;
    for(int i=1;i<=q;i++)
    {
        int nxt=upper_bound(a+1,a+1+n,mq[i].v)-a;
        for(int j=pre+1;j<nxt;j++)
        {
            pre=j;
            cal(node[j].id);
        }

        sort(k+mq[i].l,k+mq[i].r,cmpk);
        int past=1;
        int res=0;

        for(int j=mq[i].l;j<mq[i].r;j++)
        {
            int w=findfa(k[j]);
            if(past<w)
            {
                res=max(res,query_max(1,1,sz,past,w-1));
            }
            past=w+1;
        }
        if(past<sz+1)
        {
            res=max(res,query_max(1,1,sz,past,sz));
        }

        ans[mq[i].id]=res;
    }
    for(int i=1;i<=q;i++)
    {
        printf("%d\n",ans[i]);
    }


}

用multiset的

#include <bits/stdc++.h>
using namespace std;
#define mk make_pair
typedef long long ll;
typedef pair<int,int> PII;
const int N = 2e5+10;
const int M = 5e5+10;

typedef struct point
{
    int first;
    int second;
    int id;
    friend bool operator<(point a,point b)
    {
        return a.first<b.first;
    }

}point;

point node[N];

int n,m,kk;

int a[N],sz[N];

int vis[N];

vector<int> edge[N];

int fa[N];

int k[N];
bool del[N];

multiset<int> p;

map<int,int> mp[N];

int ans[M],val[N];

typedef struct query
{
    int v;
    int l,r;
    int id;
}query;

query mq[M];
//set<int> father;

int findfa(int x)
{
    if(x==fa[x]) return x;
    int nex=fa[x];
    fa[x]=findfa(nex);
    return fa[x];
}

bool cmp(query a,query b)
{
    return a.v<b.v;
}

bool cmpk(int a,int b)
{
    return findfa(a)<findfa(b);
}


void combine(int u,int v)
{
    if(sz[u]<sz[v]) swap(u,v);  //启发式合并
    sz[u]+=sz[v];
    sz[v]=0;
    fa[v]=u;
    p.erase(p.find(val[u]));
    p.erase(p.find(val[v]));
    for(auto s:mp[v])
    {
        int su=0;
        if(mp[u].count(s.first))
        {
            su=mp[u][s.first];
            if(!su) val[u]-=1;
        }

        su=(su+s.second)%kk;
        if(!su) val[u]+=1;
        mp[u][s.first]=su;

    }
    p.insert(val[u]);
    val[v]=0;


}

void cal(int x)
{
    vis[x]=1;
    p.insert(val[x]);
    for(auto s:edge[x])
    {
        if(vis[s]){
            int fx=findfa(x);
            int fy=findfa(s);
            if(fx!=fy) combine(fx,fy);
        }
    }
}




int main()
{
    scanf("%d%d%d",&n,&m,&kk);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&node[i].first);
        fa[i]=i;
        node[i].id=i;
        vis[i]=0;
        sz[i]=1;
        del[i]=false;
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&node[i].second);
        mp[i][node[i].second]=1%kk;
        val[i]=(kk==1?1:0);
    }


    sort(node+1,node+1+n);
    for(int i=1;i<=n;i++) a[i]=node[i].first;
    for(int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        edge[u].push_back(v);
        edge[v].push_back(u);
    }

    int q,cnt;
    cnt=0;
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
    {
        int tmp;
        scanf("%d%d",&mq[i].v,&tmp);
        mq[i].l=cnt;
        mq[i].r=cnt+tmp;
        mq[i].id=i;
        while(cnt<mq[i].r)
        {
            scanf("%d",&k[cnt]);

            cnt++;
        }
    }
    sort(mq+1,mq+1+q,cmp);
    int pre=0;
    for(int i=1;i<=q;i++)
    {
        int nxt=upper_bound(a+1,a+1+n,mq[i].v)-a;
        for(int j=pre+1;j<nxt;j++)
        {
            pre=j;
            cal(node[j].id);
        }

        for(int j=mq[i].l;j<mq[i].r;j++)
        {
            int w=findfa(k[j]);
            if(vis[k[j]]&&!del[w])
            {
                p.erase(p.find(val[w]));
                del[w]=true;
            }
        }
        if(p.empty()) ans[mq[i].id]=0;
        else ans[mq[i].id]=*(p.rbegin());

        for(int j=mq[i].l;j<mq[i].r;j++)
        {
            int w=findfa(k[j]);
            if(del[w])
            {
                p.insert(val[findfa(w)]);
                del[w]=false;
            }
        }
    }
    for(int i=1;i<=q;i++)
    {
        printf("%d\n",ans[i]);
    }


}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值