【省内训练2018-12-21】Connection

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39972971/article/details/85215029

【思路要点】

  • 首先考虑某一种颜色,若该颜色在各连通块中的出现次数为 { x 1 , x 2 , . . . , x m } \{x_1,x_2,...,x_m\} ,则该颜色对答案的贡献应为 i = 1 m j = i + 1 m x i x j = ( i = 1 m x i ) 2 i = 1 m x i 2 2 \sum_{i=1}^{m}\sum_{j=i+1}^{m}x_i*x_j=\frac{(\sum_{i=1}^{m}x_i)^2-\sum_{i=1}^{m}x_i^2}{2} ,因此我们只需要考虑各连通块各颜色出现次数平方的和即可。
  • 考虑一棵树的做法,显然直接启发式合并 m a p map 或是线段树合并即可计算每一棵子树中各颜色出现次数平方的和。
  • 对原图建一棵圆方树,套用树的做法即可。
  • 时间复杂度为 O ( M + N L o g 2 N ) O(M+NLog^2N) O ( M + N L o g N ) O(M+NLogN)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e5 + 5;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
struct Inf {
	ll sum;
	map <int, int> cnt;
	void clear() {
		cnt.clear();
		sum = 0;
	}
	void add(int x, int y) {
		int tmp = cnt[x];
		sum -= 1ll * tmp * tmp;
		cnt[x] += y, tmp = cnt[x];
		sum += 1ll * tmp * tmp;
	}
} panorama, sub;
struct Ans {
	ll sum, msum;
	map <int, int> cnt;
	int size() {
		return cnt.size();
	}
	void clear() {
		cnt.clear(), sum = 0;
		msum = sub.sum;
	}
	void add(int x, int y) {
		int tmp = cnt[x];
		sum -= 1ll * tmp * tmp;
		tmp = sub.cnt[x] - tmp;
		msum -= 1ll * tmp * tmp;
		cnt[x] += y, tmp = cnt[x];
		sum += 1ll * tmp * tmp;
		tmp = sub.cnt[x] - tmp;
		msum += 1ll * tmp * tmp;
	}
	void merge(Ans &from) {
		for (map <int, int> :: iterator x = from.cnt.begin(); x != from.cnt.end(); x++)
			add((*x).first, (*x).second);
	}
} res[MAXN];
int n, m, tot, timer, col[MAXN], dfn[MAXN], low[MAXN];
int top, Stack[MAXN]; ll psum, ans[MAXN];
vector <int> a[MAXN], b[MAXN];
void getans(int pos, int fa) {
	res[pos].clear();
	if (pos <= n) res[pos].add(col[pos], 1);
	ans[pos] += sub.sum - 1;
	for (unsigned i = 0; i < b[pos].size(); i++)
		if (b[pos][i] != fa) {
			getans(b[pos][i], pos);
			ans[pos] -= res[b[pos][i]].sum;
			if (res[b[pos][i]].size() > res[pos].size()) swap(res[pos], res[b[pos][i]]);
			res[pos].merge(res[b[pos][i]]);
		}
	ans[pos] -= res[pos].msum;
}
void work(int pos) {
	sub.add(col[pos], 1);
	Stack[++top] = pos;
	dfn[pos] = low[pos] = ++timer;
	for (unsigned i = 0; i < a[pos].size(); i++)
		if (dfn[a[pos][i]] == 0) {
			work(a[pos][i]);
			chkmin(low[pos], low[a[pos][i]]);
			if (low[a[pos][i]] == dfn[pos]) {
				int tmp = pos;
				b[++tot].push_back(tmp);
				b[tmp].push_back(tot);
				while (tmp != a[pos][i]) {
					tmp = Stack[top--];
					b[tot].push_back(tmp);
					b[tmp].push_back(tot);
				}
			}
		} else chkmin(low[pos], dfn[a[pos][i]]);
}
int main() {
	freopen("connection.in", "r", stdin);
	freopen("connection.out", "w", stdout);
	read(n), read(m), tot = n;
	for (int i = 1; i <= n; i++) {
		read(col[i]);
		panorama.add(col[i], 1);
	}
	for (int i = 1; i <= m; i++) {
		int x, y; read(x), read(y);
		a[x].push_back(y);
		a[y].push_back(x);
	}
	ll psum = panorama.sum;
	for (int i = 1; i <= n; i++)
		if (dfn[i] == 0) {
			sub.clear();
			work(i);
			psum -= sub.sum;
			getans(i, 0);
		}
	for (int i = 1; i <= n; i++)
		printf("%lld\n", (ans[i] + psum) / 2);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/85215029