P7265 Look At The Sky

[题目通道](Look At The Sky - 洛谷)

套路二合一

nkans=∑G∑v∈Gvknkans=G∑​v∈G∑​vk

=∑v=1nvkfv=v=1∑n​vkfv​

fvfv​ 表示数量 vv 对答案的贡献

考虑选 vv 个点硬点他们连通,有 fv=(nv)gvhn−vfv​=(vn​)gv​hn−v​,其中 gvgv​ 表示 vv 个点连通的方案数, hvhv​ 表示 vv 个点的方案数

考虑每条边选或不选有 hv=2v(v−1)2hv​=22v(v−1)​

gvgv​ 是城市规划问题,没做过建议先写这题

于是我们 O(nlog⁡n)O(nlogn) 计算出所有 fvfv​

接下来考虑如何对所有的 kk 求解

暴力求解是 O(kn)O(kn) 的,卡了一年常没卡进去

幂函数是很烦的,考虑套路第二类斯特林数展开(证明可以参考这篇题解

ans=∑v=1nfv∑j=0kS2(k,j)j!(vj)ans=v=1∑n​fv​j=0∑k​S2​(k,j)j!(jv​)

=∑j=0kS2(k,j)∑v=1nfvv!(v−j)!=j=0∑k​S2​(k,j)v=1∑n​fv​(v−j)!v!​

后面明显是个差卷积,没学过可以写一下

那么可以对所有的 jj 计算出后面的值 xjxj​

则 ans=∑j=0kS2(k,j)xjans=j=0∑k​S2​(k,j)xj​

那么只需要知道第二类斯特林数就可以计算答案了

考虑斯特林数的组合意义, nn 个球放入 mm 个盒子,那么第 nn 个可以和前面的一起或者新加一个盒子

那么 S2(n,m)=S2(n−1,m)×m+S2(n−1,m−1)S2​(n,m)=S2​(n−1,m)×m+S2​(n−1,m−1)

边界条件 S2(n,0)=[n=0]S2​(n,0)=[n=0]

可以递推得到所有所需的第二类斯特林数。本题可能卡空间,可以滚动数组优化。

总复杂度 O(nlog⁡n+k2)O(nlogn+k2)

#include <bits/stdc++.h>
using namespace std;
typedef unsigned int ui;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
std::mt19937 rnd(time(0));
inline int sj(int n)
{
	unsigned int x=rnd();
	return x%n+1;
}
#define rand fst
template<typename typC> void read(register typC &x)
{
	register int c=getchar(),fh=1;
	while ((c<48)||(c>57))
	{
		if (c=='-') {c=getchar();fh=-1;break;}
		c=getchar();
	}
	x=c^48;c=getchar();
	while ((c>=48)&&(c<=57))
	{
		x=x*10+(c^48);
		c=getchar();
	}
	x*=fh;
}
template<typename typC> void write(register typC x)
{
	if (x<0) putchar('-'),x=-x;
	static int st[100];
	register int tp=1,y;st[1]=x%10;x/=10;
	while (x) y=x/10,st[++tp]=x-y*10,x=y;++tp;
	while (--tp) putchar(st[tp]|48);
}
template<typename typC> void write(register typC *a,register int num)
{
	for (register int i=1;i<=num;i++) write(a[i]),putchar(i==num?10:32);
}
#define space(x) write(x),putchar(32)
#define enter(x) write(x),putchar(10)
const int N=262145<<1,p=998244353;
inline void inc(register int &x,const int y)
{
	if ((x+=y)>=p) x-=p;
}
inline void dec(register int &x,const int y)
{
	if ((x-=y)<0) x+=p;
}
inline int ksm(register int x,register int y)
{
	register int r=1;
	while (y)
	{
		if (y&1) r=(ll)r*x%p;
		x=(ll)x*x%p;y>>=1;
	}
	return r;
}
int a[N],b[N];
int f[N],ff[N],x[N],g[N],mi[N],r[N],yg[N],ig[N],inv[N],ifac[N],fac[N],s[2][5002];
int n,m,i,j,l,biglim,ans;
inline void ycl(int l,int limit)
{
	for (int i=1;i<limit;i++) r[i]=r[i>>1]>>1|(i&1)<<l;
}
inline void getg(int limit)
{
	ig[limit]=ksm(yg[limit]=ksm(3,(p-1)/limit),p-2);
	for (int i=limit>>1;i;i>>=1)
	{
		yg[i]=(ll)yg[i<<1]*yg[i<<1]%p;
		ig[i]=(ll)ig[i<<1]*ig[i<<1]%p;
	}
}
void dft(int *a,int xs,int limit)
{
	int i,j,k,l,w,wn,b,c;
	for (i=1;i<limit;i++) if (i<r[i]) swap(a[i],a[r[i]]);
	for (i=1;i<limit;i=l)
	{
		l=i<<1;
		if (xs) wn=yg[l]; else wn=ig[l];
		for (j=0;j<limit;j+=l)
		{
			w=1;
			for (k=0;k<i;k++,w=(ll)w*wn%p)
			{
				b=a[j|k];c=(ll)a[j|k|i]*w%p;
				a[j|k]=(b+c)%p;
				a[j|k|i]=(b+p-c)%p;
			}
		}
	}
	if (!xs)
	{
		xs=ksm(limit,p-2);
		for (i=0;i<limit;i++) a[i]=(ll)a[i]*xs%p; 
	}
}
inline void ginv(int n)
{
	inv[1]=ifac[0]=ifac[1]=1;
	for (int i=2;i<=n;i++) ifac[i]=(ll)ifac[i-1]*(inv[i]=p-(ll)p/i*inv[p%i]%p)%p;
}
inline void ji(int *a,int n)
{
	for (int i=n+1;i;i--) a[i]=(ll)a[i-1]*inv[i]%p;
	a[0]=1;
}
inline void dao(int *a,int n)
{
	for (int i=0;i<n;i++) a[i]=(ll)a[i+1]*(i+1)%p;
	a[n]=0;
}
void getinv(int *f,int *g,int biglim)
{
	int i,l=1,limit,j;
	g[0]=ksm(f[0],p-2);
	for (i=2;i<=biglim;i=limit,l++)
	{
		limit=i<<1;
		memcpy(x,f,limit<<1);
		ycl(l,limit);
		dft(x,1,limit);dft(g,1,limit);
		for (j=0;j<limit;j++) g[j]=(ll)g[j]*(2-(ll)g[j]*x[j]%p+p)%p;
		dft(g,0,limit);
		memset(g+i,0,limit<<1);
	}
}
inline int C(register int n,register int m)
{
	return (ll)fac[n]*ifac[m]%p*ifac[n-m]%p;
}
int main()
{
	read(n);read(m);
	if (n==1)
	{
		for (i=0;i<=m;i++) puts("1");return 0;
	}
	if (n==2)
	{
		puts("3");
		for (i=1;i<=m;i++) printf("%d\n",(1+ksm(ksm(2,p-2),i-1))%p);
		return 0;
	}
	fac[0]=1;
	for (i=1;i<=n;i++) fac[i]=(ll)fac[i-1]*i%p;
	ginv(n);
	for (i=0;i<=n;i++) f[i]=(ll)(ff[i]=ksm(2,((ll)i*(i-1)>>1)%(p-1)))*ifac[i]%p;
	biglim=1;
	while (biglim<=n) biglim<<=1;
	getg(biglim<<1);
	getinv(f,g,biglim);
	dao(f,n);
	biglim<<=1;
	dft(f,1,biglim);dft(g,1,biglim);
	for (i=0;i<biglim;i++) f[i]=(ll)f[i]*g[i]%p;
	dft(f,0,biglim);
	ji(f,n);
	for (i=1;i<=n;i++) f[i]=(ll)f[i]*fac[i]%p;
	for (i=1;i<=n;i++) f[i]=(ll)f[i]*ff[n-i]%p*C(n,i)%p*fac[i]%p;
	f[0]=0;memset(f+n+1,0,sizeof(f)-(n+1<<2));
	memset(g,0,sizeof(g));
	for (i=0;i<=n;i++) g[n-i]=f[i];
	memset(f,0,sizeof(f));
	for (i=0;i<=n;i++) f[i]=ifac[i];
	dft(f,1,biglim);dft(g,1,biglim);
	for (i=0;i<biglim;i++) f[i]=(ll)f[i]*g[i]%p;
	dft(f,0,biglim);
	memset(g,0,sizeof(g));
	for (i=0;i<=n;i++) g[n-i]=f[i];
	register int i,j;int nn=n,mm=m;
	register int n=nn,m=mm,ans=0,y;
	s[0][0]=1;
	for (j=0;j<=m;j++)
	{
		ans=0;y=j&1;
		for (i=0;i<=j;i++) ans=(ans+(ll)s[y][i]*g[i])%p;
		s[y^1][0]=0;++j;
		for (i=1;i<=j+1;i++) s[y^1][i]=((ll)s[y][i]*i+s[y][i-1])%p;
		--j;ans=(ll)ans*ksm(ksm(n,j),p-2)%p;printf("%d\n",ans);
	}
}

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OpenGL中的`lookAt()`函数主要用于设置观察点、目标点以及视点方向,它是一个非常重要的三维变换函数,常用于创建相机视角。通过指定这三个关键点,可以生成一组矩阵(通常是正交投影矩阵和平行投影矩阵),进而控制摄像机的位置、角度及视野范围。 **基本语法**: ```cpp gluLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ); ``` 这里的参数分别代表: - `eyeX`, `eyeY`, `eyeZ`: 观察点 (camera) 的位置坐标; - `centerX`, `centerY`, `centerZ`: 目标点的位置坐标; - `upX`, `upY`, `upZ`: 表示向上向量,即垂直于观察者视线的一个单位向量,默认值为 (0, 1, 0),通常用于确定垂直于屏幕的方向。 `lookAt()` 函数通过这些参数构建出一个透视或正交视图矩阵,这个矩阵随后会被传递给其他渲染函数如`glMultMatrix()`,用于将场景数据转换到当前视图空间内进行绘制。通过调整观察点、目标点以及向上向量,用户可以动态地改变视角,实现三维图形的全方位展示。 **使用示例**: ```cpp // 创建 OpenGL 环境并初始化上下文... glMatrixMode(GL_MODELVIEW); // 设置模型视图模式 glLoadIdentity(); // 初始化视图矩阵为单位矩阵 // 设置观察点为中心,目标点为正前方,向上向量为默认值 gluLookAt(0, 0, 5, 0, 0, 0, 0, 1, 0); // 绘制场景... glutSwapBuffers(); ``` 在这个例子中,摄像头位于坐标轴原点上方5单位的地方,朝向原点,并且垂直向量默认指向屏幕顶部。 --- ## 相关问题 - OpenGL lookAt 1. **如何自定义向上向量?** - 可以直接在`lookAt()`函数中修改第三个参数组`upX`, `upY`, `upZ`的值来自定义向上向量,这会影响到相机视角在水平面内的定向。 2. **如何仅更改观察点而不移动目标点?** - 修改`eyeX`, `eyeY`, `eyeZ`即可实现观察点的移动,而保持`centerX`, `centerY`, `centerZ`不变,则目标点不会跟着动。 3. **`lookAt()`函数和`perspective()`函数有何区别?** - `lookAt()`主要用于设定相机的位置、目标点以及视野的方向,它是构建视图矩阵的一部分;`perspective()`则用于创建透视投影矩阵,结合视图矩阵一同完成从世界坐标系到屏幕坐标系的转换过程。两者共同作用决定了最终图像的呈现效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值