专栏文章

题解:AT_agc036_f [AGC036F] Square Constraints

AT_agc036_f题解参与者 2已保存评论 2

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
2 条
当前快照
1 份
快照标识符
@minclyce
此快照首次捕获于
2025/12/02 00:13
3 个月前
此快照最后确认于
2025/12/02 00:13
3 个月前
查看原文
从 CSP-S2025 第四题的容斥做法过来的。
i2+pi2i^2+p_i^2 联想到线段长的平方,那么两边的限制在二维平面上围成了一个同心圆,外圆半径为 2n2n,内圆半径为 nn。那么 (i,pi)(i,p_i) 就应该位于这个同心圆内部,可以得到 pip_i 的范围 [li,ri][l_i,r_i]。注意下标从 00 开始。
同时我们注意到,随着 ii 的增加,pip_i 的范围区间是单调的。并且当 i[n,2n)i\in[n,2n) 时,li=0l_i=0,当 i[0,n)i\in[0,n) 时,li>0l_i>0
如果每个 pip_i 的下界 lil_i 都是 00,那么我们将所有 rir_i 排序后,答案就是 (rii+1)\prod(r_i-i+1)
如果存在下界,那么刚才的办法就行不通,因为我们无法得知当前的数有多少种选择方案。因此我们考虑lil_i 非零的 ii 容斥,将 [li,ri][l_i,r_i] 容斥为 [0,ri][0,r_i] 的方案减去 [0,li1][0,l_i-1] 的方案。假设我们算出了选择了 kkii 并容斥他们的范围为 [0,li1][0,l_i-1] 的方案数 fkf_k,那么他会对答案造成 (1)kfk(-1)^kf_k 的贡献。
然而就算我们进行了容斥,由于一个 ii 需要考虑 [0,ri][0,r_i][0,li1][0,l_i-1] 两种情况,我们不便在 dp 时准确地得知每个位置可以填的数的方案数。但是……这真的不能做到吗?
为了实现这一点,我们考虑更换排序方式,对于 li=0l_i=0ii,他们不需要容斥,将他们的 rr 作为关键字,对于 li>0l_i>0 的,需要考虑容斥,将他们的 l1l-1 作为第一关键字,rr 作为第二关键字,两种情况放到一起排序。然后我们设 fi,jf_{i,j} 表示考虑了排序后的前 ii 个数,选择了 jj 个数使他们的范围变为 [0,l1][0,l-1] 的方案数。
那么考虑加入第 i+1i+1 个数,设之前有 xxli=0l_i=0 的数,有 yyli>0l_i>0 的数,分三种情况:
\bulletli+1=0l_{i+1}=0,则 fi,j×(ri+1+1jx)fi+1,jf_{i,j}\times(r_{i+1}+1-j-x)\to f_{i+1,j},当前数的可选择的数为 ri+1\le r_{i+1}ri+1+1r_{i+1}+1 个数,减去前面选择的 jj 个范围为 [0,l1][0,l-1] 的数(他们排完序以后,一定有 lp1ri+1l_p-1\le r_{i+1}),再减去前面其他的 l=0l=0 的数。
\bulletli+1>0l_{i+1}>0,并选择他的范围为 [0,li+11][0,l_{i+1}-1],则 fi,j×(li+1jx)fi+1,j+1f_{i,j}\times (l_{i+1}-j-x)\to f_{i+1,j+1},选择方案数同上一种情况分析。
\bulletli+1>0l_{i+1}>0,并选择他的范围为 [0,ri+1][0,r_{i+1}],则 fi,j×(ri+1+1nk(yj))fi+1,jf_{i,j}\times (r_{i+1}+1-n-k-(y-j))\to f_{i+1,j},这种情况的选择方案数,包括 ri+1\le r_{i+1}ri+1+1r_{i+1}+1 个数,减去 l=0l=0nn 个数,减去全局选择了范围为 [0,l1][0,l-1]kk 个数,再减去前面 l>0l>0 但选择了范围为 [0,r][0,r]yjy-j 个数。由于 l>0l>0 的数的 rr 一定满足 r3n>lmaxr\ge\sqrt 3n>lmax,所以只有后面 l=0l=0 的且选择了范围为 [0,r][0,r] 数不会占用当前数的选择方案。
一次转移是 O(n2)O(n^2) 的,由于第三种情况方案数计算时需要一个确定的 kk,所以我们需要在最外层再枚举选择范围为 [0,l1][0,l-1] 的个数 kk,总复杂度为 O(n3)O(n^3)
CPP
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=500+5;
int n;ll mod,f[N][N],ss;
struct node{
	int l,r;
	bool operator <(const node x)const{
		if(!l&&!x.l)return r<x.r;
		if(!l)return r==x.l-1?l<x.r:r<x.l-1;
		if(!x.l)return l-1==x.r?r<x.l-1:l-1<x.r;
		else return x.l==l?r<x.r:l<x.l;
	}
}a[N];
int main(){
	scanf("%d%lld",&n,&mod);
	for(int i=0;i<n*2;i++){
		if(i<n)a[i+1].l=ceil(sqrt(n*n-i*i));
		a[i+1].r=min(int(floor(sqrt(4*n*n-i*i))),2*n-1);
	}
	sort(a+1,a+2*n+1);
	for(int k=0;k<=n;k++){
		memset(f,0,sizeof(f));f[0][0]=1;
		int ca=0,cb=0;
		for(int i=1;i<=n*2;i++){
			for(int j=0;j<=cb;j++){
				if(!f[i-1][j])continue;
				if(a[i].l){
					f[i][j]=(f[i][j]+f[i-1][j]*(a[i].r+1-k-n-(cb-j)))%mod;
					if(j!=k)f[i][j+1]=(f[i][j+1]+f[i-1][j]*(a[i].l-j-ca))%mod;
				}
				else f[i][j]=(f[i][j]+(a[i].r+1-ca-j)*f[i-1][j])%mod;
			}
			if(a[i].l)cb++;else ca++;
		}
		if(k&1)ss=(ss-f[2*n][k]+mod)%mod;
		else ss=(ss+f[2*n][k])%mod;
	}
	cout<<ss<<endl;
	return 0;
}

评论

2 条评论,欢迎与作者交流。

正在加载评论...