Loading

乐堕的魔法

总结

8.00~8.40

\(T1\) ,但咋都不会多项式复杂度的做法。
很快想到相当于找一棵生成树,如果颜色不同边权为1,否则为0,然后再加上不在树上的颜色个数。
突然发现,因为一种颜色一定在最右边被加进树里,所以只需要强制要求这棵树包含每种颜色最右边的点即可。
然后就可以线性 \(dp\) 了。

8.00~9.10

写了个 \(T2\)\(O(n^3)\) 暴力。

9.10~10.00

\(T2\) 相当于给了六个不等式,那么我可以容斥一个子集,然后开始大分讨,各种容斥,都不行。

10.00~10.20

\(T1\) 写了个拍,发现数组开小了。

10.20~11.30

开始想 \(T3\) 的做法,推着推着发现了关键性质,然后就可以做到 \(O(n^2)\)
细节有点多。

11.30~12.00

因为是一堆组合数求和,所以感觉可以卷积,但是推了半天不会。

12.00~12.30

写了个 \(T2\)\(O(n^2\log n)\)暴力 。

题解

T2

越想越蠢。
题解也还是容斥,但是容斥的和我想的不太一样。
首先加上 \(\binom{n}{3}\),然后考虑减掉多算的。
如果有一个数把另外两个严格偏序,那么是多算的,可以用 \(cdq\) 分治求出被 \(i\) 三维偏序的个数 \(c_i\),然后减掉 \(\binom{c_i}{2}\)
如果有一个数恰好两维把另外两个严格偏序,那么可以用钦定两个的(二维偏序)减掉钦定三个的 \((三维偏序)\),所以复杂度 \(O(n\log ^2n)\)

code
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+7;
typedef long long LL;
struct node 
{
	int a,b,c,v;
}p[N],A[N],B[N],q[N],t[N];
int n;
bool atc(node x,node y)
{
	return x.a<=y.a&&x.b<=y.b&&x.c<=y.c;
}
int a=0,b=0;
bool cmp(node x,node y)
{
	if(x.a!=y.a) return x.a<y.a;
	return x.b<y.b;
}
int c[N];
void upd(int x,int v)
{
	for(int i=x;i<=n;i+=i&-i)
	c[i]+=v;
}
int ask(int x)
{
	int res=0;
	for(int i=x;i;i-=i&-i)
	res+=c[i];
	return res;
} 
LL solve()
{
	LL res=0;
	int i=1,j=1;
	while(i<=a||j<=b)
	{
		if((i>a)||((j<=b)&&A[i].b>B[j].b)) 
		{
			upd(B[j++].c,1);
		}
		else 
		{
			res+=ask(A[i++].c+1);
		}
	}
	return res;
}
LL ans;
void cdq(int l,int r)
{
	if(l==r)return;
	int mid=(l+r)>>1;
	cdq(l,mid);cdq(mid+1,r);
	int i=l,j=mid+1,k=l;
	while(i<=mid||j<=r)
	{
		if(j>r||(i<=mid&&p[i].b<=p[j].b))
		{
			upd(p[i].c,1);
			q[k++]=p[i++];
		}
		else 
		{
			p[j].v+=ask(p[j].c);
			q[k++]=p[j++];
		}
	}
	for(i=l;i<=mid;i++)upd(p[i].c,-1);
	for(i=l;i<=r;i++)p[i]=q[i];
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	scanf("%d %d %d",&p[i].a,&p[i].b,&p[i].c);
	ans=n;
	ans+=1ll*n*(n-1)/2ll; 
	sort(p+1,p+n+1,cmp);cdq(1,n);
	ans+=1ll*n*(n-1)*(n-2)/6;
	for(int i=1;i<=n;i++)
	{
		int c=p[i].v;
		ans-=c;
		ans+=2ll*c*(c-1)/2;
	}
	for(int i=1;i<=n;i++)t[i]=p[i],t[i].v=0;
	for(int i=1;i<=n;i++)p[i]=t[i],p[i].c=n;
	sort(p+1,p+n+1,cmp);cdq(1,n);
	for(int i=1;i<=n;i++)
	{
		int c=p[i].v;
		ans-=1ll*c*(c-1)/2;
	}
	for(int i=1;i<=n;i++)p[i]=t[i],p[i].b=n;
	sort(p+1,p+n+1,cmp);cdq(1,n);
	for(int i=1;i<=n;i++)
	{
		int c=p[i].v;
		ans-=1ll*c*(c-1)/2;
	}
	for(int i=1;i<=n;i++)p[i]=t[i],p[i].a=n;
	sort(p+1,p+n+1,cmp);cdq(1,n);
	for(int i=1;i<=n;i++)
	{
		int c=p[i].v;
		ans-=1ll*c*(c-1)/2;
	}
	cout<<ans;
	return 0;
}

T3

太牛了!

相当于有 \(n\) 个区间 \([a_{2i-1},a_{2i}]\),然后令 \(x=a_0\),依次 \(\forall i\in [1,n]\) ,如果 \(x<a_{2i-1},x=a_{2i-1}\),如果 \(x>a_{2i},x=a_{2i}\)

考虑对于一个固定的 \(x\) 求答案。

注意到若区间 \([l,r]\) 满足 \(l<x<r\) ,我们删掉这个区间不影响答案,这些区间称为五小区间,那么删掉之后剩下了若干 \(r<x\) 的区间和若干 \(l>x\) 的区间。

假设 \(x\) 是某个区间的右端点,左端点类似。
考虑 \(x\) 在排列中的位置 \(i\),那么要求 \((i,n]\) 这些区间都是无效区间 ,并且 \(i\) 左面第一个非无效区间的 \(l>x\) 或者第一个非无效区间的位置是 \(a_0\)

那么就可以计数,首先特判掉 \(a_0=x\) 的情况,此时 \(x=n\),贡献 \(n!n!\)
然后枚举极长的的无效区间后缀长度 \(i\),然后把 \(x\) 所在区间插进去,
\(x\) 是右端点,贡献为:

\[\binom{x}{i}\binom{2n-x}{i}i!i!\binom{2n-x-i}{2}(x-i)(i+1)f_{2n-2i-3} \]

其中 \(f_n\) 为把 \(n\) 个数排成 \(a_{2i-1}<a_{2i}\) 的方案数,显然有递推 \(f_{n}=\binom{n}{2}f_{n-2}\),展开后 \(f_n=\frac{n!}{2^{n/2}}\)

推一波式子:

\[\sum_i \binom{x}{i}\binom{2n-x}{i}i!i!\binom{2n-x-i}{2}(x-i)(i+1)f_{2n-2i-3} \]

\[\sum_i \frac{x!(2n-x)!}{(x-i)!(2n-x-i)!}\binom{2n-x-i}{2}(x-i)(i+1)f_{2n-2i-3} \]

\[x!(2n-x)!\sum_i \frac{(2n-x-i)(2n-x-i-1)}{(x-i)!(2n-x-i)!2^{n-i-1}}(x-i)(i+1)(2n-2i-3)! \]

\(x\) 为区间左端点,贡献是类似的,为:

\[x!(2n-x)!\sum_i \frac{(x-i)(x-i-1)}{(x-i)!(2n-x-i)!2^{n-i-1}}(2n-x-i)(i+1)(2n-2i-3)! \]

加起来得到:

\[x!(2n-x)!\sum_i \frac{(x-i)(2n-x-i)(2n-2i-2)!}{(x-i)!(2n-x-i)!2^{n-i-1}}(i+1) \]

\[x!(2n-x)!\sum_i \frac{(2n-2i-2)!}{(x-i-1)!(2n-x-i-1)!2^{n-i-1}}(i+1) \]

\[x!(2n-x)!\sum_{i>0} \frac{(2n-2i)!}{(x-i)!(2n-x-i)!2^{n-i}}i \]

\[\frac{1}{2^n}x!(2n-x)!\sum_{i>0} \binom{2n-2i}{x-i}2^{i}i \]

还有一种情况是所有区间都是无效区间,此时也满足 \(x=n\),贡献也是好算的,我们只需要处理上面的求和式。

\[f_{x,p,q}=\sum_{i=1}^{n-1}i^p2^i\binom{2n-2i-q}{x-i} \]

其中 \(p,q\in \{0,1\}\)

我们考虑从 \(f_x\) 推到 \(f_{x+1}\) ,我们可以得到如下式子:

\[\sum_{i=1}^{n-1}i^p2^i\binom{2n-2i}{x-i}=\sum_{i=1}^{n-1}i^p2^i\binom{2n-2i-1}{x-1-i}+\sum_{i=1}^{n-1}i^p2^i\binom{2n-2i-1}{x-i} \]

\(f_{x,p,0}=f_{x-1,p,1}+f_{x,p,1}\)

\[\sum_{i=1}^{n-1}i2^i\binom{2n-2i-1}{x-i}=\frac{1}{2}\sum_{i=2}^{n}(i-1)2^i\binom{2n-2i+1}{x-i+1} \]

\[\sum_{i=1}^{n-1}i2^i\binom{2n-2i-1}{x-i}=\frac{1}{2}\sum_{i=2}^{n}(i-1)2^i\binom{2n-2i}{x-i}+\frac{1}{2}\sum_{i=2}^{n}(i-1)2^i\binom{2n-2i}{x-i+1} \]

\[2\sum_{i=1}^{n-1}i2^i\binom{2n-2i-1}{x-1-i}=\sum_{i=2}^{n}(i-1)2^i\binom{2n-2i}{x-1-i}+\sum_{i=2}^{n}(i-1)2^i\binom{2n-2i}{x-i} \]

\[2f_{x-1,1,1}=f_{x-1,1,0}-f_{x-1,0,0}+f_{x,1,0}-f_{x,0,0} \]

此外还有:

\[\sum_{i=1}^{n-1}2^i\binom{2n-2i-1}{x-i}=\frac{1}{2}\sum_{i=2}^{n}2^i\binom{2n-2i+1}{x-i+1} \]

\[2\sum_{i=1}^{n-1}2^i\binom{2n-2i-1}{x-i}=\sum_{i=2}^{n}2^i\binom{2n-2i}{x-i}+\sum_{i=2}^{n}2^i\binom{2n-2i}{x-i+1} \]

\[2\sum_{i=1}^{n-1}2^i\binom{2n-2i-1}{x-1-i}=\sum_{i=2}^{n}2^i\binom{2n-2i}{x-1-i}+\sum_{i=2}^{n}2^i\binom{2n-2i}{x-i} \]

\[2f_{x-1,0,1}=f_{x-1,0,0}-2\binom{2n-2}{x-2}+f_{x,0,0}-2\binom{2n-2}{x-1} \]

需要注意一点, \(\binom{2n-2n}{x-n}\) 只有在 \(x=n\) 时才有值,因此需要对 \(x=n\) 特判一下,减掉一个常数。

有了四个式子,我们就可以 \(O(n)\) 递推了。

code
#include<bits/stdc++.h>
using namespace std;
const int N = 2e7+7;
int fac[N],ifac[N];
const int mod = 998244353;
int Pow(int a,int b)
{
	int res=1;
	while(b)
	{
		if(b&1)res=1ll*res*a%mod;
		a=1ll*a*a%mod;
		b>>=1;
	}
	return res;
}
void init(int n)
{
	fac[0]=1;
	for(int i=1;i<=n;i++)fac[i]=1ll*fac[i-1]*i%mod;
	ifac[n]=Pow(fac[n],mod-2);
	for(int i=n-1;i>=0;i--)ifac[i]=1ll*ifac[i+1]*(i+1)%mod;
}
int binom(int n,int m)
{
	if(n<0||m<0||n<m)return 0;
	return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}
int n,m;
int f[N/2][2][2]; 
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline int sub(int a,int b){return a-b<0?a-b+mod:a-b;}
inline int mul(int a,int b){return 1ll*a*b%mod;}
int main()
{
	cin>>n>>m;
	init(2*n+1);
	int inv2=Pow(2,mod-2);
	inv2=Pow(inv2,n);
	int pw=Pow(2,n);
	for(int x=1;x<=n;x++)
	{
		f[x][0][0]=add(sub(mul(2,f[x-1][0][1]),f[x-1][0][0]),mul(2,binom(2*n-1,x-1)));
		if(x==n+1||x==n)f[x][0][0]=sub(f[x][0][0],pw);
		f[x][1][0]=add(f[x][0][0],add(mul(2,f[x-1][1][1]),sub(f[x-1][0][0],f[x-1][1][0])));
		if(x==n+1||x==n)f[x][1][0]=sub(f[x][1][0],1ll*(n-1)*pw%mod);
		f[x][0][1]=sub(f[x][0][0],f[x-1][0][1]);
		f[x][1][1]=sub(f[x][1][0],f[x-1][1][1]);
	}
	while(m--)
	{
		int x;
		scanf("%d",&x);
		x=min(x,2*n-x);
		int ans=f[x][1][0];
		int coef=1ll*fac[x]*fac[2*n-x]%mod;
		ans=1ll*ans*coef%mod*inv2%mod;
		if(x==n) ans=(ans+1ll*fac[n]%mod*fac[n]%mod);
		if(x==n) 
		{
			ans=(ans+1ll*coef*2*n%mod)%mod;
		}
		printf("%d ",ans);
	}
	return 0;
}
posted @ 2024-02-18 18:37  Larunatrecy  阅读(49)  评论(0)    收藏  举报