【AtCoder Beginner Contest 252】部分题解

D - Distinct Trio

题意:
给定一个长度为 n n n的序列 a a a,求使得 1 ≤ i < j < k ≤ n 1\leq i1i<j<kn,且 a i , a j , a k a_i,a_j,a_k ai,aj,ak互不相同的方案数
数据范围: 3 ≤ n ≤ 2 × 1 0 5 , 1 ≤ a i ≤ 2 × 1 0 5 3\leq n\leq 2\times 10^5, 1\leq a_i\leq 2\times 10^5 3n2×105,1ai2×105

题解:
先计算每个值出现的次数,排序去重
令前缀中任意两个值不同的数构成的方案数为 p p r e ppre ppre
前缀中数的个数 p r e pre pre

这里可以利用到类似背包优化的思想来倒序解决问题
i i i种数的个数为cnt[i]
i i i种数构成的方案数:cnt[i] × 前i-1种数构成的任意两种数的方案数
所以需要维护前i-1种数构成的任意两种数的方案数
前i种数构成的任意两种数的方案数:cnt[i] × i-1种数的个数

代码:

#include
using namespace std;

typedef long long ll;

const int N = 2e5 + 10;
int a[N], n;
int cnt[N], g;
ll pre[N], ppre[N];

void solve() {
	scanf("%d", &n);
	for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
	sort(a + 1, a + n + 1);
	for(int i = 1; i <= n; ++i) {
		int j = i + 1;
		while(j <= n && a[j] == a[i]) j += 1;
		cnt[++g] = j - i;
		i = j - 1;
	}
	
	if(g < 3) {
		puts("0");
		return ;
	}
	
	ll res = 0;
	pre[2] = cnt[1] + cnt[2];
	pre[1] = cnt[1];
	ppre[2] = 1ll * cnt[1] * cnt[2];
	for(int i = 3; i <= n; ++i) {
		res += ppre[i - 1] * cnt[i];
		ppre[i] = ppre[i - 1] + cnt[i] * pre[i - 1];
		pre[i] = pre[i - 1] + cnt[i];
	}
	printf("%lld\n", res);
}

int main()
{
	int T = 1;
//	scanf("%d", &T);
	for(int i = 1; i <= T; ++i) solve();

	return 0;
}

补充思路:
2022/06/06
思路来源于 Codeforces1598 D
正难则反,考虑选择 3 3 3 个数共有 n × ( n − 1 ) × ( n − 2 ) 6 \frac{n\times (n-1)\times (n-2)}{6} 6n×(n1)×(n2) 种,减去存在相同数的方案即可,而这里的难点也在于如何减去存在相同数的方案。
这里可以枚举所有数量大于等于 2 2 2 的数 x x x ,然后枚举第三个数 y y y

  • 如果 y ≠ x y \neq x y=x,则 y y y n − c n t x n - cnt_x ncntx 个选择,方案数为 C c n t x 2 × ( n − c n t x ) C_{cnt_x}^2 \times (n-cnt_x) Ccntx2×(ncntx)
  • 如果 y = x y=x y=x,方案数为 C c n t x 3 C_{cnt_x}^{3} Ccntx3

代码:

#include
using namespace std;

typedef long long ll;

const int N = 200010;
ll a[N], n;

void solve() {
	scanf("%lld", &n);
	for (int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
	sort(a + 1, a + n + 1);
	
	ll ans = n * (n - 1) * (n - 2) / 6;
	for (int i = 1; i <= n; ++i) {
		int j = i + 1;
		while (j <= n && a[j] == a[i]) j += 1;
		
		ll c = j - i;
		if (c >= 2) {
			ll temp = c * (c - 1) / 2;
			temp *= (n - c);
			ans -= temp;
			
			if (c > 2) {
				ans -= c * (c - 1) * (c - 2) / 6;
			}	
		}
		
		i = j - 1;
	}
	
	printf("%lld\n", ans);
}

int main()
{
	int T = 1;
//	scanf("%d", &T);
	for (int i = 1; i <= T; ++i) {
		solve();
	}

	return 0;
}

E - Road Reduction

题意:
给定一个 n n n m m m边的带权无向连通图,现在要求保留 n − 1 n-1 n1条边, d i d_i di表示从 1 1 1 i i i的距离,使得最小化 ∑ i = 2 n d i \sum\limits_{i=2}^n d_i i=2ndi

数据范围:
1 ≤ n ≤ 2 × 1 0 5 1\leq n\leq 2\times 10^5 1n2×105
n − 1 ≤ m ≤ 2 × 1 0 5 n-1\leq m\leq 2\times 10^5 n1m2×105
1 ≤ c i ≤ 1 0 9 1\leq c_i\leq 10^9 1ci109

题解:
考虑从 1 1 1到每个点 i i i的最短距离,可以由从起点为 1 1 1开始跑一遍 d i j k s t r a dijkstra dijkstra,就正好可以获得 d i d_i di
而根据 d i j k s t r a dijkstra dijkstra的更新情况,每个点都会被至少松弛一次,最后被松弛的那次对应一条边,而 1 1 1是起点,不会被其他边松弛,这样 n − 1 n-1 n1个点正好对应 n − 1 n-1 n1条边。

代码:

#include
using namespace std;

typedef long long ll;

const int N = 200010;
const int M = N << 1;
int h[N], e[M], ne[M], w[M], idx;
int n, m; 

struct Node {
	int id;
	ll d;
	bool operator < (const Node& A) const {
		return d > A.d;
	}
};

void add(int a, int b, int c) {
	e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}

int pre[N];
ll dist[N];
bool st[N];
priority_queue<Node> q;
void dijkstra(int fir) {
	while(!q.empty()) q.pop();
	for(int i = 1; i <= n; ++i) {
		dist[i] = 1e18;
		st[i] = false;
	}
	
	dist[fir] = 0;
	q.push({fir, dist[fir]});
	
	while(!q.empty()) {
		Node t = q.top(); q.pop();
		int u = t.id;
		if(st[u]) continue ;
		st[u] = true;
		
		for(int i = h[u]; i != -1; i = ne[i]) {
			int v = e[i];
			if(dist[v] > dist[u] + w[i]) {
				dist[v] = dist[u] + w[i];
				pre[v] = i;
				q.push({v, dist[v]});
			}
		}
	}
}

void solve(int ca) {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; ++i) h[i] = -1;
	idx = 0;
	for(int i = 1; i <= m; ++i) {
		int a, b, c;
		scanf("%d%d%d", &a, &b, &c);
		add(a, b, c);
		add(b, a, c);
	}
	
	dijkstra(1);
	for(int i = 2; i <= n; ++i) printf("%d%c", pre[i] / 2 + 1, " \n"[i == n]);
	
}

int main()
{
	int T = 1;
//	scanf("%d", &T);
	for(int i = 1; i <= T; ++i) solve(i);

	return 0;
}

你可能感兴趣的:(#,算法,图论,数据结构)