题目大意

  有一个两部各有 \(n\) 个节点的二分图 \(G\),定义 \(G^m\) 为一个 \(m+1\) 层的图,每层有 \(n\) 个节点,相邻两层的诱导子图都和 \(G\) 相同。

  给你 \(m\),求对于所有 \(1\leq i\leq m\),\(G^i\) 的最小生成树的边权和。

  保证图连通。

  \(n,m\leq 100000,\text{边数 }\leq 200000,\text{边权}\leq 30\)

题解

  对于 \(G^i\),先求出用了多少种边权 \(<j\) 的边,再求出用了多少条边权 \(\leq j\) 的边,就可以得到用了多少条边权为 \(j\) 的边。

  那么边权就可以忽略了。

  现在要求出 \(G^i\) 有多少条边。

  从左往右扫,用并查集维护最后两层节点的连通性。

  那么再下一层的并查集肯定会是这两层的并查集加上一点边。

  当我们处理完一层的时候,求出这层新加的边对下一层的贡献。

  这层每加一条边,下一层就要在这两个集合右侧的点之间连边。

  然后不停地往右边传就好了。

  每加一条边就会合并两个集合,所以总共会加 \(O(n)\) 条边。

  时间复杂度:\(O(w(n+m+e)\alpha(n))\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<functional>
#include<cmath>
#include<vector>
#include<assert.h>
//using namespace std;
using std::min;
using std::max;
using std::swap;
using std::sort;
using std::reverse;
using std::random_shuffle;
using std::lower_bound;
using std::upper_bound;
using std::unique;
using std::vector;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef std::pair<int,int> pii;
typedef std::pair<ll,ll> pll;
void open(const char *s){
#ifndef ONLINE_JUDGE
char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout);
#endif
}
void open2(const char *s){
#ifdef DEBUG
char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout);
#endif
}
int rd(){int s=0,c,b=0;while(((c=getchar())<'0'||c>'9')&&c!='-');if(c=='-'){c=getchar();b=1;}do{s=s*10+c-'0';}while((c=getchar())>='0'&&c<='9');return b?-s:s;}
void put(int x){if(!x){putchar('0');return;}static int c[20];int t=0;while(x){c[++t]=x%10;x/=10;}while(t)putchar(c[t--]+'0');}
int upmin(int &a,int b){if(b<a){a=b;return 1;}return 0;}
int upmax(int &a,int b){if(b>a){a=b;return 1;}return 0;}
const int N=200010;
int f[N];
vector<pii> g[40],a,b;
int find(int x)
{
return f[x]==x?x:f[x]=find(f[x]);
}
int c[N];
int merge(int x,int y)
{
if(find(x)==find(y))
return 0;
if(!c[find(y)])
c[find(y)]=c[find(x)];
f[find(x)]=find(y);
return 1;
}
int n,m,e;
ll ans[N];
ll s[N];
ll d[N];
int main()
{
open("c");
scanf("%d%d%d",&n,&m,&e);
int x,y,w;
for(int i=1;i<=e;i++)
{
scanf("%d%d%d",&x,&y,&w);
g[w].push_back(pii(x,y));
}
for(int i=1;i<=30;i++)
{
a.clear();
for(int j=1;j<=i;j++)
for(auto v:g[j])
a.push_back(v);
for(int j=1;j<=2*n;j++)
f[j]=j;
for(int j=1;j<=m;j++)
d[j]=0;
for(int j=1;j<=2*n;j++)
c[j]=0;
for(auto v:a)
d[1]+=merge(v.first,v.second+n);
b.clear();
for(int j=n+1;j<=2*n;j++)
if(!c[find(j)])
c[find(j)]=j;
else
b.push_back(pii(c[find(j)]-n,j-n));
for(int j=2;j<=m;j++)
{
a=b;
b.clear();
for(auto v:a)
if(find(v.first)!=find(v.second))
{
if(c[find(v.first)]&&c[find(v.second)])
b.push_back(pii(c[find(v.first)]-n,c[find(v.second)]-n));
merge(v.first,v.second);
}
else
d[j]--;
}
for(int j=2;j<=m;j++)
d[j]+=d[j-1];
for(int j=2;j<=m;j++)
d[j]+=d[j-1];
for(int j=1;j<=m;j++)
{
ans[j]+=i*(d[j]-s[j]);
s[j]=d[j];
}
}
for(int i=1;i<=m;i++)
printf("%lld\n",ans[i]);
return 0;
}

【XSY3345】生成树 并查集的更多相关文章

  1. HDU 4786 生成树 并查集+极大极小值 黑白边 确定选择白边的数量

    题意: 给定一个无向图 n 个点 m条无向边 u v val val == 1 表示边(u, v) 为白边 问能否找到n个点的生成树, 使得白边数为斐波那契数 思路: 并查集求图是否连通( 是否存在生 ...

  2. BZOJ 3624: [Apio2008]免费道路 [生成树 并查集]

    题意: 一张图0,1两种边,构造一个恰有k条0边的生成树 优先选择1边构造生成树,看看0边是否小于k 然后保留这些0边,补齐k条,再加1边一定能构成生成树 类似kruskal的证明 #include ...

  3. hdu 1272 判断所给的图是不是生成树 (并查集)

    判断所给的图是不是生成树,如果有环就不是,如果没环但连通分量大于1也不是 find函数 用递归写的话 会无限栈溢出 Orz要加上那一串 手动扩栈 Sample Input6 8 5 3 5 2 6 4 ...

  4. 离线+生成树+并查集——cf1213G

    #include<bits/stdc++.h> using namespace std; #define N 200005 #define ll long long struct Edge ...

  5. 图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用

    图的连通性问题:无向图的连通分量和生成树,所有顶点均由边连接在一起,但不存在回路的图. 设图 G=(V, E) 是个连通图,当从图任一顶点出发遍历图G 时,将边集 E(G) 分成两个集合 T(G) 和 ...

  6. UVA 1395 苗条的生成树(最小生成树+并查集)

    苗条的生成树 紫书P358 这题最后坑了我20分钟,怎么想都对了啊,为什么就wa了呢,最后才发现,是并查集的编号搞错了. 题目编号从1开始,我并查集编号从0开始 = = 图论这种题真的要记住啊!!题目 ...

  7. 利用并查集求最大生成树和最小生成树(nlogn)

    hdu1233 还是畅通工程 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) T ...

  8. Codevs 3287 货车运输 2013年NOIP全国联赛提高组(带权LCA+并查集+最大生成树)

    3287 货车运输 2013年NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 传送门 题目描述 Description A 国有 n 座 ...

  9. 货车运输-洛谷-1967-LCA+最大生成树(kruskal(并查集))

    传送门 一道:LCA+最大生成树 个人认为把这两个的板子写好(并熟练掌握了之后)就没什么难的 (但我还是de了好久bug)qwq 最大生成树:其实就是最小生成树的变形 我用的是kruskal (个人觉 ...

随机推荐

  1. C#的一些获取时间的例子

    从周一到周日的顺序,获取排序数值: int i = DateTime.Now.DayOfWeek - DayOfWeek.Monday; if (i == -1) i = 6; 获取某日起,星期一日期 ...

  2. Ubunttu16.04升级/更新git版本(亲测有效)

    sudo add-apt-repository ppa:git-core/ppa sudo apt-get update sudo apt-get install git 升级前: 升级后:

  3. VSCode瞎折腾记

    搬到小机房后终于能用VSCode啦(没错以前的系统是xp) 但是这东西比Dev难搞多了qwq,简单记一下自己的DIY历程吧(不然全搞炸就凉了) 设置语言为中文 可以直接下载插件 让VSCode支持编译 ...

  4. http协议、模块、express框架以及路由器、中间件和mysql模块

    一.http协议 是浏览器和web服务器之间的通信协议 1.通用头信息 request url:请求的url,向服务器请求的数据 request method:请求的方式   get.post sta ...

  5. 微信小程序 选择微信自带的地址 用户授权选择了拒绝

    // 选择微信自带地址 addAddr:function () { wx.chooseAddress({ success: function (res) { self.setData({ addrIn ...

  6. C#实现多级子目录Zip压缩解压实例

          参考 https://blog.csdn.net/lki_suidongdong/article/details/20942977 重点: 实现多级子目录的压缩,类似winrar,可以选择 ...

  7. Snapde和常用的CSV文件编辑器对比

    Snapde,一个专门为编辑超大型数据量CSV文件而设计的单机版电子表格软件:它运行的速度非常快,反应非常灵敏. CSV是一种用逗号分隔列.回车分割行的文本文件,市面上常用的CSV编辑软件有:Snap ...

  8. ios View 向上拉界面源码

    如下的资料是关于ios View 向上拉界面的代码. #pragma mark - 上升效果- (void)ToUpSide {          } - (void)moveToUpSide {   ...

  9. 自己动手写事件总线(EventBus)

    本文由云+社区发表 事件总线核心逻辑的实现. EventBus的作用 Android中存在各种通信场景,如Activity之间的跳转,Activity与Fragment以及其他组件之间的交互,以及在某 ...

  10. Flume1.9.0的安装、部署、简单应用(含分布式、与Hadoop3.1.2、Hbase1.4.9的案例)

    目录 目录 前言 什么是Flume? Flume的特点 Flume的可靠性 Flume的可恢复性 Flume的一些核心概念 Flume的官方网站在哪里? Flume在哪里下载以及如何安装? 设置环境变 ...