1725C. Circular Mirror 组合,需要认真的分析并划分情况

文章讲述了在圆上给n个点染m种颜色的复杂问题,确保任何能构成三角形的点颜色不同。通过分析直径对的数量和颜色分布,利用组合数学中的C函数和阶乘计算染色方案总数。
摘要由CSDN通过智能技术生成

Problem - 1725C - Codeforces

题目大意:

是一个圆上有n个点,我们有m种颜色,去给这些点染色。

能构成三角形的点不能是同一种颜色。

思路:

我们应该知道圆的直径和圆上其余任意一点都能构成三角形。

如何处理这个染色其实蛮复杂的。不过做法就是正面去做去分析。

——

首先我们可以统计出有多少对直径,这里设为 c 对。(用set统计,本人一开始暴力超时了,,)

——

我们分析可知,三角形有两种情况:

1,直径的两点不同,第三点随意。

2,直径两点相同,第三点必须与之不同。(这种情况要求各直径颜色都不同,因为互相构成三角形。所以需要占据某种颜色

——分类

所以我们先划分直径点对。

c对中有 i 对时相同的,剩下c-i 对就是不同的。

(这里 i 可以小,不一定可以大,因为要占据颜色a种。我们最后再分析。)

剩下c - i 对,每对不同即可,互相不干扰。

以及剩下 n - c*2 个单独的点,他们在 m - i 种颜色中自由选择。

——式子

(C是组合函数,C(c,i)指c对里面选i个,a是阶乘,见板子排列组合板子 A C-CSDN博客)

所以直径颜色相同 i 对时总数为:
C ( c , i ) ∗ C ( m , i ) ∗ a [ i ] ∗ ( m − i ) c − i ∗ ( m − i − 1 ) c − i ∗ ( m − i ) n − c ∗ 2 C(c,i ) * C(m, i ) * a[ i ] * (m - i)^{c-i} *(m-i-1)^{c-i}*(m-i)^{n-c*2} C(c,i)C(m,i)a[i](mi)cimi1ci(mi)nc2

——i的取值

i 的上限应为 min( m , c )

此处有异议,因为当我们 c 大于 m 时,我们可以有 i == m - 1 对颜色相同,剩下只有 1 种颜色了,而我们剩下的 c - (m-1) 对需要颜色不同,至少也得两种颜色啊。

其实我们看式子,当 i 为 m - 1 时,有一项 m - i - 1 就为0了,那么整个式子就是0了,所以这种情况不会影响我们的答案。
这种情况也是不会出现的。
所以 i == m 时,剩下的没颜色选了,也是+0种,不影响答案。
所以 i <= min( m , c )
(取c的话颜色就充足了嘛,冲突的不会计入的)

参考代码:

#define ll long long
#define endl "\n"
#define int long long
const ll inf = 1e9;
const ll MOD = 998244353;

int a[300005];
ll ksm(int x, int y, int mod)
{
	if (x == 1) return 1;
	ll res = 1, base = x;
	while (y) {
		if (y & 1) res = (res * base) % mod;
		base = (base * base) % mod;
		y >>= 1;
	}
	return res;
}
ll C(ll n, ll m, ll p)
{
	if (m > n)return 0;
	return ((a[n] * ksm(a[m], p - 2, p)) % p * ksm(a[n - m], p - 2, p) % p);
}
ll A(ll n, ll m, ll p)
{
	if (m > n)return 0;
	return (a[n] * ksm(a[n - m], p - 2, p)) % p;
}
//距离为周长一半即是半径
void solve()
{
	int n, m;
	cin >> n >> m;
	int sum = 0;
	vector<int>arr(n + 1);
	for (int i = 1; i <= n; i++)
	{
		cin >> arr[i];
		sum += arr[i];
	}
	if (sum % 2 == 0)
	{
		int couple = 0;
		//vector<int>vis(n + 1);
		set<int>vis;
		int cur = 0;
		for (int i = 1; i <= n; i++)
		{
			cur += arr[i];
			if (vis.count(cur))
			{
				couple++;
			}
			vis.insert(cur + sum / 2);
		}

		int c = n - couple * 2;
		int ans = 0;
		for (int i = 0; i <= min(couple,m)/* && i <= m - 2||i==couple&&m-i>=1||n==couple*2&&m>=couple+2&&i<=couple*/; i++)
		{
			ans = (ans + C(couple, i, MOD) * a[i] % MOD * C(m, i, MOD) % MOD
				//选出a对,m中选出a种颜色,a的阶乘种
				* ksm(m-i,couple-i,MOD) % MOD * ksm(m-i-1,couple-i,MOD) % MOD
				* ksm(m - i, n - couple * 2, MOD) % MOD) % MOD;
		}
		cout << ans;
	}
	else//没有直径
	{
		cout << ksm(m, n, MOD) % MOD;
	}
}
signed main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	a[0] = a[1] = 1;
	for (int i = 2; i <= 300000; i++)
	{
		a[i] = a[i - 1] * i % MOD;
	}
	int t = 1;
	//cin >> t;
	while (t--)
	{
		solve();
	}
	return 0;
}
  • 29
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值