[Ctsc2014]图的分割

本文深入探讨了图的分割算法,特别关注通过最小生成树的最大边权来确定连通组件的有效方法。文章提供了详细的数学证明,并附带了一个C++实现示例,用于演示如何根据特定条件合并图的连通块。

[Ctsc2014]图的分割

阅读理解好题

翻译一下:

M(C)就是C这个诱导子图最小生成树最大边权

结论:

按照w进行sort,如果满足w<=Ci,Cj表示u,v的连通块的诱导子图

并且Ci!=Cj那么进行连边

证明:

只需要证明两点:

1.某个边如果现在需要连边(不连就不满足半完美定义),那么以后也一定需要连边

也即,不能<=w

Z是不单调的,但是一直是正整数,而之后再进行合并,w越来越大,M(Ci)一定会一直>=w

所以不会更小

2.某个边如果现在连上了边,那么以后也不可能可以断开。

本质和1一样的。。。

所以这个构造方法一定是正确的!

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define fi first
#define se second
#define mk(a,b) make_pair(a,b)
#define numb (ch^'0')
#define pb push_back
#define solid const auto &
#define enter cout<<endl
#define pii pair<int,int>
using namespace std;
typedef long long ll;
template<class T>il void rd(T &x){
    char ch;x=0;bool fl=false;
    while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+'0');}
template<class T>il void ot(T x){if(x<0) putchar('-'),x=-x;output(x);putchar(' ');}
template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar('\n');}

namespace Miracle{
const int N=100000+5;
const int M=500000+5;
int n,m;
struct node{
    int a,b,w;
    bool friend operator <(node a,node b){
        return a.w<b.w;
    }
}e[M];
int fa[N],sz[N],mx[N];
int z[N];
int fin(int x){
    return fa[x]==x?x:fa[x]=fin(fa[x]);
}
vector<int>mem[N];
int main(){
    rd(n);rd(m);
    for(reg i=1;i<=n;++i) rd(z[i]);
    for(reg i=1;i<=m;++i){
        rd(e[i].a);rd(e[i].b);rd(e[i].w);
    }
    sort(e+1,e+m+1);
    for(reg i=1;i<=n;++i){
        fa[i]=i;sz[i]=1;
    }
    for(reg i=1;i<=m;++i){
        int x=e[i].a,y=e[i].b;
        int k1=fin(x),k2=fin(y);
        if(k1!=k2){
            if(e[i].w<=min(mx[k1]+z[sz[k1]],mx[k2]+z[sz[k2]])){
                fa[k1]=k2;
                sz[k2]+=sz[k1];
                mx[k2]=e[i].w;
            }
        }
    }
    int cnt=0;
    for(reg i=1;i<=n;++i){
        int k=fin(i);
        if(!mem[k].size()) ++cnt;
        mem[k].push_back(i);
    }
    printf("%d\n",cnt);
    for(reg i=1;i<=n;++i){
        if(mem[i].size()){
            printf("%d ",mem[i].size());
            for(reg j=0;j<(int)mem[i].size();++j){
                printf("%d ",mem[i][j]);
            }
            puts("");
        }
    }
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
*/

 

转载于:https://www.cnblogs.com/Miracevin/p/10839042.html

LGP10006[集训队互测 2023] 超现实树 NOI/NOI+/CTSC O2优化 2023 WC/CTSC/集训队 点分治 根号分治 快速数论变换 NTT 集训队互测 标准IO 传统题 来源 洛谷 时间限制 1000ms 内存限制 512MB 通过/尝试次数 0/46 题目背景 Alek 喜欢打信息竞赛,尤其喜欢超现实树。超现实树,顾名思义,就是树上的超现实数。 题目描述 Alek 认为,对于常数 𝑘 k,一个字符串被称为「 𝑘 k-超现实数串」,如果其只包含字符 { , | , } {,|,},且: 空串为 𝑘 k-超现实数串; 如果 𝑠 , 𝑡 s,t 为 𝑘 k-超现实数串,那么 𝑠 + 𝑡 s+t 为 𝑘 k-超现实数串; 如果 𝑘 + 1 k+1 个字符串 𝑠 1 , 𝑠 2 , ⋯   , 𝑠 𝑘 + 1 s 1 ​ ,s 2 ​ ,⋯,s k+1 ​ 都是 𝑘 k-超现实数串,那么 { + 𝑠 1 + | + 𝑠 2 + | + ⋯ + | + 𝑠 𝑘 + 1 + } {+s 1 ​ +|+s 2 ​ +|+⋯+|+s k+1 ​ +} 为 𝑘 k-超现实数串; 𝑘 k-超现实数串仅限于此。 给定一棵 𝑛 n 个点的无根树,节点编号为 1 ∼ 𝑛 1∼n。每个点 𝑖 i 上有一个字符 𝑎 𝑖 ∈ { { , | , } } a i ​ ∈{{,|,}}。 给定整数 𝑚 m,Alek 希望你对 𝑘 = 0 , 1 , ⋯   , 𝑚 k=0,1,⋯,m 分别求出:有多少有序对 ( 𝑥 , 𝑦 ) (x,y), 1 ≤ 𝑥 , 𝑦 ≤ 𝑛 1≤x,y≤n,使得树上从点 𝑥 x 到点 𝑦 y 的唯一简单路径上的字符依次拼接所得字符串是 𝑘 k-超现实数串。 输入格式 第一行两个整数 𝑛 , 𝑚 n,m,分别表示树的节点数,和需要求答案的 𝑘 k 的上限。 第二行一个字符串 𝑎 a, 𝑎 a 的第 𝑖 i 个字符表示点 𝑖 i 上的字符。 接下来 𝑛 − 1 n−1 行,每行两个整数 𝑥 , 𝑦 x,y,表示存在一条连接点 𝑥 x 和点 𝑦 y 的边。 输出格式 输出一行 𝑚 + 1 m+1 个整数,分别表示 𝑘 = 0 , 1 , ⋯   , 𝑚 k=0,1,⋯,m 时的答案。 输入 #1 运行 复制 5 3 |{}}} 2 1 3 2 4 1 5 1 输出 #1 复制 1 2 0 0 输入 #2 运行 复制 10 8 |}||}{|{{{ 2 1 3 1 4 3 5 2 6 5 7 5 8 4 9 2 10 3 输出 #2 复制 2 0 1 1 0 0 0 0 0 输入 #3 运行 复制 见附加文件 ex_surreal3.in。 输出 #3 复制 见附加文件 ex_surreal3.ans。 提示 对于所有数据,有 2 ≤ 𝑛 ≤ 1 0 5 2≤n≤10 5 , 0 ≤ 𝑚 ≤ 𝑛 − 2 0≤m≤n−2, 𝑎 𝑖 ∈ { { , | , } } a i ​ ∈{{,|,}}。 Subtask 1(5 分): 𝑛 ≤ 4601 n≤4601; Subtask 220 分):对每条边 ( 𝑥 , 𝑦 ) (x,y) 有 𝑦 = 𝑥 + 1 y=x+1; Subtask 3(5 分): 𝑎 𝑖 ≠ | a i ​  =|, 𝑚 = 0 m=0; Subtask 4(15 分,依赖 Subtask 3): 𝑚 ≤ 3 m≤3; Subtask 5(25 分,依赖 Subtask 1): 𝑛 ≤ 5 × 1 0 4 n≤5×10 4 ; Subtask 6(30 分,依赖 Subtask 1, 2, 3, 4, 5):无特殊限制。 C++写代码
06-03
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值