其他分享
首页 > 其他分享> > 「AGC036F」Square Constraints 题解

「AGC036F」Square Constraints 题解

作者:互联网

「AGC036F」Square Constraints 题解

题目大意

给定一个整数 $ n $,求有多少种 $ 0\ -\ 2n!-!1 $ 的排列 $ P $,使得对于每个 $ i $,都有 $ n^2 \le i^2 + P_i^2 \le 4n^2 $。输出答案对给定的 $ m $ 取余的结果

输入

两个整数,$ n \(,\) m $。

输出

一个整数,表示答案。


思路

初始想法

通过 $ n^2 \le i^2 + P_i^2 \le 4n^2 $ 这个式子,我们很容易想到用 $ i^2 + P_i^2 \le 4n^2 $ 的方案数减去 $ i^2 + P_i^2 < n^2 $ 的方案数。但是我们发现实际上不可做。不过这种想法提示我们可以考虑容斥

深入思考

首先,我们提炼一下问题模型:

我们考虑这个问题的弱化版:

我们考虑将 $ a $ 数组从小到大排序,那么这个弱化版的答案为:

\[\underset{i=0}{\overset{2n-1}{\prod}}a_i-i \]

证明:

首先将 $ a $ 数组从小到大排序。

对于这个排列 $ P $,我们肯定先从 $ a $ 小的开始放。所以第一步的方案数为 $ a_0 $ 。

再考虑下一个数 $ 1 $。此时剩下 $ 2n-1 $ 个数。因为 $ a $ 从小到大,所以上一轮选走的数一定小于等于 $ a_1 $。此时的方案数为 $ a_1-1 $。

因为这些步骤是递进的,所以方案数是每一步的方案数的乘积。最终方案数即为:

\[a_0 \times (a_1-1) \times (a_2-2) \times \dots \times (a_{2n-1}-2n+1)\\ =\underset{i=0}{\overset{2n-1}{\prod}}a_i-i \]

证毕。

而原问题模型多了一个限制条件,不难想到将满足两个条件的最大值都设出来,综合在一起,套用公式得到答案


回到本题,我们将满足条件的最大值设出来。

我们尝试设 $ F(i) $ 为满足 $ i^2 + P_i^2 \le 4n^2 $ 的 $ P_i $ 的最大值, $ G(i) $ 为满足 $ i^2 + P_i^2 < n^2 $ 的 $ P_i $ 的最大值。其中 $ i \in [0,2n-1] $。

通过观察我们不难发现:

  1. $ F $ 和 $ G $ 两个函数值随着 $ i $ 的增大而减小。

    $ n^2 $ 和 $ i^2 $ 为定值,$ i $ 增大,则 $ i^2 $ 增大,那么 $ P_i^2 $ 就会减小。

  2. $ G(i) $ 满足 $ i \in [0,n-1] $。

    当 $ i \ge n $ 时,$ i^2 \ge n^2 $,要使 $ i^2 + P_i^2 < n^2 $,则 $ P_i^2 < 0 $,无解。

  3. $ \max_{i=0}^{n-1}(G_i) < \min_{i=0}^{n-1}(F_i) $。

    由于第一条规律:$ \max_{i=0}^{n-1}(G_i) $ 即 $ G_0 \(,\) \min_{i=0}^{n-1}(F_i) $ 即 $ F_{n-1} $,

    $ \because 0^2 + G_0^2 < n^2 , n^2 + F_{n-1}^2 \le 4n^2 $

    $ \therefore G_0^2 < n^2 , F_{n-1}^2 \le 3n^2 $

    $ \because n^2 \le 3n^2 $

    $ \therefore G_0^2 < F_{n-1}^2 \le 3n^2 $

    $ \because G_0 \in \mathbb{N} ,F_{n-1} \in \mathbb{N} $

    $ \therefore G_0 < F_{n-1} $

    $ \therefore \max_{i=0}^{n-1}(G_i) < \min_{i=0}^{n-1}(F_i) $

有了这些东西,我们就可以将限制拆分为:

所有的数都满足 $ i^2 + P_i^2 \le 4n^2 $,且至少有 $ k $ 个数满足 $ i^2 + P_i^2 < n^2 $,也就是说在前面 $ n $ 个位置选出 $ k $ 个 $ G(x) $ 。

然后就可以进行容斥了。

什么?你不会容斥?你可以翻到最下面的补充知识

在弱化版中,我们将 $ a $ 数组从小到大进行了排序。

所以我们类比一下,考虑将 $ F $ 和 $ G $ 两个函数打包成二元组排序。由于第三条规律,我们通过如下方式打包成二元组:

接下来按第一关键字进行排序。

接下来就是求解方案数。我们可以使用 $ DP $ 求解。

设 $ f_{i,j} $ 表示在前 $ i $ 个位置上选出了 $ j $ 个 $ G(x) $ 的方案数。

在状态转移时需要用到当前数在选出的位置上的排名,所以用两个变量 $ t1 $ 和 $ t2 $,分别统计前面 $ (F(x),0) $ 和 $ (G(x),F(x)) $ 的个数。

首先枚举 $ k $,从 $ 0 $ 到 $ n $,表示在前面 $ n $ 个位置选出 $ k $ 个 $ G(x) $;

然后枚举 $ i $,从 $ 0 $ 到 $ 2n-1 $,表示已经确定了前面 $ i $ 个位置;

最后枚举 $ j $,从 $ 0 $ 到 $ k $,表示前面已经选出了 $ j $ 个 $ G(x) $;

状态转移分情况讨论:

最后综合起来求出 $ f_{2n-1,k} $,容斥即可。

代码实现

#include<bits/stdc++.h>
#define ll long long
const ll N=501;
using namespace std;

ll n,p;
ll f[N][N];
vector<pair<ll,ll>>t;//记录二元组 

ll F(ll i){//F函数 
	ll ans=2*n-1;
	while(ans>=0&&i*i+ans*ans>4*n*n)ans--;//注意是大于 
	return ans+1;
}

ll G(ll i){//G函数 
	ll ans=2*n-1;
	while(ans>=0&&i*i+ans*ans>=n*n)ans--;//注意是大于等于 
	return ans+1;
}

int main(){
	
	scanf("%lld%lld",&n,&p);
	for(ll i=0;i<2*n;i++){//预处理二元组 
		if(i<n)t.push_back({G(i),F(i)});
		else t.push_back({F(i),0});
	}
	sort(t.begin(),t.end());//对二元组按第一关键字排序 
	ll ans=0;
	for(ll k=0;k<=n;k++){
		//初始化 
		memset(f,0,sizeof(f));
		f[0][0]=1;
		
		ll t1=0,t2=0;//辅助统计变量 
		for(ll i=0;i<t.size();i++){
			for(ll j=0;j<=k;j++){
				if(!t[i].second){//(F(i),0) 
					f[i+1][j]=(f[i+1][j]+f[i][j]*(t[i].first-j-t1)%p)%p;//选F(i) 
				}else{//(G(i),F(i)) 
					if(j<k){//选了之后不能超过k个 
						f[i+1][j+1]=(f[i+1][j+1]+f[i][j]*(t[i].first-j-t1)%p)%p;//选G(i) 
					}
					f[i+1][j]=(f[i+1][j]+f[i][j]*(t[i].second-n-k-t2+j)%p)%p;//选F(i) 
				}
			}
			if(!t[i].second)t1++;
			else t2++;
		}
		//容斥 
		if(k%2)ans=(ans-f[t.size()][k]+p)%p;
		else ans=(ans+f[t.size()][k])%p;
	}
	printf("%lld",ans);
	
	return 0;
}

补充知识

容斥原理

容斥原理是一种组合数学常用的方法。

比如说,你要计算几个集合并集的大小,我们可以先将所有单个集合的大小加起来,然后减去所有两个集合相交的部分,再加回所有三个集合相交的部分,再减去所有四个集合相交的部分……依此类推,最终你就可以得出答案。

用数学公式可以表示为:

\[\left | \bigcup_{i=1}^{n}a_i \right | = \underset{T \subseteq \{1,2,...,n\}}{\sum}(-1)^{|T|-1} \left | \bigcap_{i \in T} a_i \right | \]

广义容斥原理

容斥原理求的是不满足任何性质的方案数,我们通过计算所有至少满足 $ k $ 个性质的方案数之和来计算。

同样的,我们可以通过计算所有至少满足 $ k $ 个性质的方案数之和来计算恰好满足 $ k $ 个性质的方案数。这样的容斥方法我们称之为广义容斥原理。

一般我们会用二项式反演进行计算。

二项式反演

对于函数 $ f(k) $ 和 $ g(k) $,满足:

\[g(k) = \underset{i=0}{\overset{k}{\sum}} C_k^i f(i) \]

则:

\[f(k) = \underset{i=0}{\overset{k}{\sum}} (-1)^{k-i} C_k^i g(i) \]

它一般用于"恰好"和"至少""至多"的转换式中


举个例子:

「bzoj2839」集合计数

一个有 $ n $ 个元素的集合有 $ 2^n $ 个不同子集(包含空集),现在要在这 $ 2^n $ 个集合中取出至少一个集合,使得它们的交集的元素个数为 $ k $ ,求取法的方案数模 $ 10^9+7 $。

$ 1 \le n \le 10^6 \(,\) 0 \le k \le n $。

由题列出式子:$ C_n^k (2{2{n-k}}-1) $。即钦定 $ k $ 个交集元素,则包含这 $ k $ 个的集合有 $ 2^{n−k} $ 个;每个集合可选可不选,但不能都不选。

设 $ f(i) $ 表示钦定交集元素为至少 $ i $ 个的方案数,$ g(i) $ 表示钦定交集元素恰好为 $ i $ 个的方案数,则有 $ C_n^k (2{2{n-k}}-1) = f(k) = \underset{i = k}{\overset{n}{\sum}} C_i^k g(i) $。

这个时候我们就可以利用二项式反演,求得 $ g(k) = \underset{i = k}{\overset{n}{\sum}} (-1)^{i-k} C_i^k f(i) = \underset{i=k}{\overset{n}{\sum}} (-1)^{i-k} C_i^k C_n^i (2{2{n-i}}-1) $。

这样就可以 $ O(n) $ 求出答案。

尾声

如果你发现了问题,你可以直接回复这篇题解

如果你有更好的想法,也可以直接回复!

标签:方案,Square,题解,ll,容斥,le,ans,2n,Constraints
来源: https://www.cnblogs.com/zsc985246/p/16621307.html