2019牛客暑期多校训练营(第九场)E All men are brothers(线段树做法)

链接:https://ac.nowcoder.com/acm/contest/889/E
来源:牛客网
 

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld

题目描述

Amy asks Mr. B  problem E. Please help Mr. B to solve the following problem.

 

There are n people, who don't know each other at the beginning.

There are m turns. In each turn, 2 of them will make friends with each other.

The friend relation is mutual and transitive.

If A is a friend of B, then B is also a friend of A.

For example, if A is a friend of B, B is a friend of C, then A and C are friends.

At the beginning and after each turn, please calculate the number of ways to select four people from, such that any two of these four are not friends.

 

输入描述:

The first line contains two integers, n and m (n <= 100000, m <= 200000), which are the number of people, and the number of turns.

In the following m lines, the i-th line contains two integers x and y ( 1 <= x <= n, 1 <= y <= n, x ≠ y), which means the x-th person and the y-th person make friends in the i-th turn.

The x-th person and y-th person might make friends in several turns.

输出描述:

Output m+1 lines, each line contains an integer, which is the number of quadruples.

Output at the beginning and after each turn, so there are m+1 lines.

示例1

输入

复制

6 6
1 2
3 4
4 5
3 5
3 6
2 4

输出

复制

15
9
4
0
0
0
0

示例2

输入

复制

100000 0

输出

复制

4166416671249975000

说明

Don't use int.

            因为组合较小(4),我们直接推两个集合融合的过程,然后加线段树支持单点修改就行了。

            推的过程字太丑,要是看代码不懂的话再留言或Q我吧。           

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
int n, m;
const int maxn = 110000;
ll c[maxn * 8][5];
int fa[maxn], sz[maxn];
int fin(int x) {
	if (x != fa[x]) {
		fa[x] = fin(fa[x]);
	}
	return fa[x];
}
void pushup(int rt) {
	c[rt][1] = c[rt << 1][1] + c[rt << 1 | 1][1];
	c[rt][2] = c[rt << 1][1] * c[rt << 1 | 1][1] + c[rt << 1][2] + c[rt << 1 | 1][2];
	c[rt][3] = c[rt << 1][1] * c[rt << 1 | 1][2] + c[rt << 1][2] * c[rt << 1 | 1][1] 
		       + c[rt << 1][3] + c[rt << 1 | 1][3];
	c[rt][4] = c[rt << 1][2] * c[rt << 1 | 1][2] + c[rt << 1][1] * c[rt << 1 | 1][3] 
	           + c[rt << 1][3] * c[rt << 1 | 1][1] + c[rt << 1][4] + c[rt << 1 | 1][4];
}
void build(int l, int r, int rt) {
	if (l == r) {
		c[rt][4] = c[rt][3] = c[rt][2] = 0;
		c[rt][1] = 1;
		return;
	}
	int m = (l + r) >> 1;
	build(lson); build(rson);
	pushup(rt);
}
void add(int l, int r, int rt,int x,ll sum) {
	if (l == r) {
		c[rt][4] = c[rt][3] = c[rt][2] = 0;
		c[rt][1] = sum;
		return;
	}
	int m = (l + r) >> 1;
	if (x <= m) 
		add(lson, x, sum);
	else add(rson, x, sum);
	pushup(rt);
}
int main() {
	int n, m;
	ios::sync_with_stdio(0);
	cin >> n >> m;
	build(1, n, 1);
	for (int i = 1; i <= n; i++)
		fa[i] = i, sz[i] = 1;
	cout << c[1][4] << "\n";
	for (int i = 0; i < m; i++) {
		int a, b;
		cin >> a >> b;
		a = fin(a); b = fin(b);
		if (a != b) {
			fa[a] = b; sz[b] += sz[a];
			add(1, n, 1, a, 0);
			add(1, n, 1, b, sz[b]);
		}
		cout << c[1][4] << "\n";
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值