计蒜客信息学赛前 CSP-S2 模拟赛 2

本来想打打计蒜客的比赛涨涨信心的,结果\(T2\)少考虑了一点东西给我整自闭了。

\(A\)

按位与的性质是,\(a \& b <= a\),所以某个点的距离就是所有直接与它相连的边的边权。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstring>

using namespace std;

const int N = 1000000;

int n, head[N + 50], num, dp[N + 50];

template <class T>
void Read(T &x)
{
	x = 0; int p = 0; char st = getchar();
	while (st < '0' || st > '9') p = (st == '-'), st = getchar();
	while (st >= '0' && st <= '9') x = (x << 1) + (x << 3) + st - '0', st = getchar();
	x = p ? -x : x;
	return;
}

template <class T>
void Print(T x)
{
	if (x > 9) Print(x / 10);
	putchar(x % 10 + '0');
	return;
}

void File()
{
	freopen("node.in", "r", stdin);
	freopen("node.out", "w", stdout);
	return;
}

struct Node
{
	int next, to, dis;
} edge[N * 2 + 50];

void Addedge(int u, int v, int w)
{
	edge[++num] = (Node){head[u], v, w};
	head[u] = num;
	return;
}

void Dfs(int x, int fa, int fadis)
{
	int maxx = fadis;
	for (int i = head[x]; i; i = edge[i].next)
	{
		int v = edge[i].to;
		if (v == fa) continue;
		maxx = max(maxx, edge[i].dis);
		Dfs(v, x, edge[i].dis);
	}
	dp[x] = maxx;
	return;
}

int main()
{ 
//	File();
	Read(n);
	for (int i = 1, u, v, w; i <= n - 1; i++)
	{
		Read(u); Read(v); Read(w);
		Addedge(u, v, w); Addedge(v, u, w);
	}
	Dfs(1, 0, 0);
	int pos = 1;
	for (int i = 2; i <= n; i++) if (dp[i] < dp[pos]) pos = i;
	printf("%d", pos);
	return 0;
}

\(B\)

考虑只有\(> k\)\(n - k\)个点会对答案产生影响,所以令\(m - n - k\),将这\(m\)个点和剩下\(k\)个点分开来讨论。

定义一段连续的放在一起的\(> k\)的点为连通块,那么最后删去的边的数量就是\(n - k -\)连通块个数。

这样就可以枚举连通块个数算算贡献。

当把这\(m\)个点分成若干块时,剩下的\(k\)个点也需要分成\(k\)块,所以最多可以分成\(min(k, m - d)\)块。

枚举最后分成的块数\(p\),那么先用隔板法分成若干个集合\(\binom{m - 1}{p - 1}\)\(\binom{k - 1}{p - 1}\),再枚举集合内部的情况乘上\(m!\)\(k!\)

然后因为环上的每个位置是不同的,所以再枚举哪个数开头将答案\(\times n\)

但是发现之前划分集合的时候,因为集合是没有区分的,所以每种方案一共算了\(p\)次,答案要除以\(p\)

这样最后就是

\[\sum_{p = 1}^{min(k, m - d)} \frac{\binom{m - 1}{p - 1} \binom{k - 1}{p - 1} \times m! \times k! \times n}{p} \]

考试的时候就差了最后除以\(p\)那一步,难受。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstring>

using namespace std;

const int N = 1000000;
const int MOD = 998244353;

template <class T>
void Read(T &x)
{
	x = 0; int p = 0; char st = getchar();
	while (st < '0' || st > '9') p = (st == '-'), st = getchar();
	while (st >= '0' && st <= '9') x = (x << 1) + (x << 3) + st - '0', st = getchar();
	x = p ? -x : x;
	return;
}

template <class T>
void Print(T x)
{
	if (x > 9) Print(x / 10);
	putchar(x % 10 + '0');
	return;
}

int jc[N + 50], divjc[N + 50], n, k, d, div[N + 50];

int Zh(int n, int m)
{
	return 1LL * jc[n] * divjc[n - m] % MOD * divjc[m] % MOD;
}

int Ksm(int a, int b)
{
	int tmp = 1;
	while (b)
	{
		if (b & 1) tmp = 1LL * tmp * a % MOD;
		a = 1LL * a * a % MOD;
		b >>= 1;	
	} 
	return tmp;
}

void File()
{
	freopen("ring.in", "r", stdin);
	freopen("ring.out", "w", stdout);
	return;
}

int main()
{
//	File();
	int t;
	Read(t);
	jc[0] = 1;
	for (int i = 1; i <= N; i++) jc[i] = 1LL * jc[i - 1] * i % MOD;
	divjc[N] = Ksm(jc[N], MOD - 2);
	for (int i = N - 1; i >= 0; i--) div[i + 1] = 1LL * divjc[i + 1] * jc[i] % MOD, divjc[i] = 1LL * divjc[i + 1] * (i + 1) % MOD;
	divjc[0] = div[0] = 1;
	while (t--)
	{
		Read(n); Read(k); Read(d);
		int m = n - k, ans = 0;
		for (int i = 1; i <= m - d; i++)
			ans = (0LL + ans + 1LL * Zh(m - 1, i - 1) * Zh(k, i - 1) % MOD * div[i] % MOD) % MOD;
		ans = 1LL * ans * n %MOD * jc[m] % MOD * jc[k] % MOD;
		printf("%d\n", ans);
	}
	return 0;
}

/*
1
4 2 1

16
*/

\(C\)

\(D\)

posted @ 2020-10-18 19:33  Tian-Xing  阅读(210)  评论(0编辑  收藏  举报