DTOJ 2022.12.24 测试 题解

(2023 省选模拟 Round #1)

测试成果 50+0+0

太菜了)

A 御神体

这题写了四个多小时,最后还是没写出来ww

莫队一直写挂(不过对莫队的理解加深了很多

题目链接

DTOJ P4346

题目大意

计算出下面这个式子的值,q 个询问
85% 数据, n,m105,q10515%数据, n,m106,q104

(i=0nij=0mi(ij)[i+j0(mod2)])mod998244353

题解

首先我观察到 n=m 很好做,答案即为 2n 可惜没部分分

接着我注意到 [i+j0(mod2)]=12(1+(1)i+j)

于是式子就可以拆成两部分做

i=0nj=0m(ij)[i+j0(mod2)]=12(i=0nj=0m(ij)+(1)i+j(ij))=12(i=0nj=0m(ij)+i=0n(1)ij=0m(1)j(ij))=12(i=0nj=0m(ij)+i=1n(1)i(1)m(i1m)+1)=12(i=0nj=0m(ij)(1)mi=0n1(1)i(im)+1)

注意这边用到了一个组合数小公式

组合数小结论 1

i=0n(1)i(ki)={(1)n(k1n)(k>0)1(k=0)

因为没注意到 k=0 所以推错了(还是太菜了ww

证明:

需要一些基本的组合数公式和求和号化简方法(

大家做组合数式子的时候可以画出杨辉三角,比较直观

要小心 (nm)=(n1m1)+(n1m) 要满足 m0

i=0n(1)i(ki)=(k10)+i=1n(1)i((k1i1)+(k1i))=i=0n1(1)i+1(k1i)+i=0n(1)i(k1i)=(1)n(k1n)

第一部分——求组合数之和

也就是求 i=0nj=0m(ij)

我们发现之前做过一题类似的 洛谷 P5388 [Cnoi2019]最终幻想

就是说组合数的一行的前缀和可以递推

记一行 rown,m=i=0m(ni)

rown,mrown,m+1 是简单的,只需要加一个 (nm+1)

rown,mrown+1,m 也不难,画出杨辉三角可以直观地看到 rown+1,m=2rown,m(nm)

(用 (nm)=(n1m1)+(n1m) 这个推)

我们来同理做做一列:

i=0n(im)=i=0n((i1m)+(i1m1))=i=0n(i1m)+i=0n(i1m1)=i=0n1(im)+i=0n1(im1)(nm)=i=0n1(im1)(nm)+(nm1)=i=0n(im1)i=0n(im)=(nm+1)+(nm)=(n+1m+1)

他甚至可以用一个组合数表示

组合数小结论 2

i=0n(im)=(n+1m+1)

于是我们用莫队就可以以 O(qm) 回答第一部分的询问

第二部分——求组合数一列的交错和

要求这个 i=0n1(1)i(im) 这个长得和蔼可亲的式子

我们记 Hn,m=i=0n(1)i(im)

首先 Hn,mHn+1,m 跟之前那样只是改了求和上标,只需加上 (1)n+1(n+1m)

Hn,mHn,m+1 我们仿照之前推一列组合数的和的式子:

Hn,m+1=i=0n(1)i(im+1)=i=0n(1)i(i1m+1)+i=0n(1)i(i1m)=i=0n1(1)i+1(im+1)+i=0n1(1)i+1(im)=i=0n1(1)i(im+1)i=0n1(1)i(im)=Hn,m+1+(1)n(nm+1)Hn,m+(1)n(nm)2Hn,m+1=(1)n(nm+1)Hn,m+(1)n(nm)

这就可以递推了

莫队

注意到可以不需要做 m-- 的操作,毕竟对 n=m 的答案可以直接出来

莫队之前一直没理解,之后看了这个动图就懂了,我觉得脑子里有这样的图就很直观了

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e6+5, M = 1e5+5, P = 998244353, fL = 3e3, inv2 = (P+1)>>1;
int fc[N],fci[N],pw2[N]; // 阶乘,阶乘逆元,2的幂
int ksm(int a, int b) { int res=1; for(; b; b>>=1,a=(ll)a*a%P) if(b&1) res=(ll)a*res%P; return res; } 
struct Ayaka { int n,m,ans,id; } rq[M]; // 存每次询问
int C(int n, int m)
{
	if(n<0 or m<0 or n<m) return 0;
	return (ll)fc[n]*fci[m]%P*fci[n-m]%P;
}
inline int pown1(int a) { return (a&1)?-1:1; } // -1 的幂
struct Kurumi
{
	int sum,h,row,col;
	int n,m;
	int query() { return (0ll+sum+1-pown1(m)*h+P)*inv2%P; } //答案可以拆成 sum 和 h 两部分
	void change_n(int d) // d=1/-1 表示加入/删去一行
	{
		if(d<0) sum=(sum-row+P)%P; // 从 sum 删去一行
		if(d>0) row=(2ll*row-C(n,m)+P)%P; // 如上述更新一行
		else row=(ll)(row+C(n-1,m))*inv2%P; // 这个就是上面的式子反过来
		if(d>0) sum=(sum+row)%P; // 往 sum 加入一行
		if(d>0) h=(0ll+h+pown1(n)*C(n,m)+P)%P; // 更新 h
		else h=(0ll+h-pown1(n-1)*C(n-1,m)+P)%P; // 这个就是上面的式子反过来
		n+=d; col=C(n+1,m+1); // 上述结论
	}
	void change_m()
	{
		row=(row+C(n,m+1))%P; col=C(n+1,m+2); sum=(sum+col)%P;
		h=((0ll-h+pown1(n-1)*(C(n-1,m)+C(n-1,m+1)))%P+P)*inv2%P;
		m++;
	}
};
signed main()
{
	fc[0]=1; for(int i=1; i<N; i++) fc[i]=(ll)fc[i-1]*i%P;
	fci[N-1]=ksm(fc[N-1],P-2); for(int i=N-1; i; i--) fci[i-1]=(ll)fci[i]*i%P;
	pw2[0]=1; for(int i=1; i<N; i++) pw2[i]=(pw2[i-1]<<1)%P; // 预处理
	int q; scanf("%d",&q);
	for(int i=1; i<=q; i++) scanf("%d%d",&rq[i].n,&rq[i].m),rq[i].id=i;
	sort(rq+1,rq+q+1, [&] (const Ayaka &a, const Ayaka &b) { if(a.n/fL!=b.n/fL) return a.n<b.n; return a.m<b.m; }); // 莫队分块
	for(int l=1,r; l<=q; l=r+1)
	{
		r=l; while(r<q and rq[r+1].m>=rq[r].m) r++; // 这边记得是 >= 不然会 tle
		int tmp=rq[l].m; Kurumi cur={pw2[tmp+1]-1,0,pw2[tmp],1,tmp,tmp}; // n=m 初始情况
		for(int i=l; i<=r; i++)
		{
			while(cur.m<rq[i].m) cur.change_m();
			while(cur.n<rq[i].n) cur.change_n(1);
			while(cur.n>rq[i].n) cur.change_n(-1);
			rq[i].ans=cur.query();
		}
	}
	sort(rq+1,rq+q+1, [&] (const Ayaka &a, const Ayaka &b) { return a.id<b.id; }); 
	for(int i=1; i<=q; i++) printf("%d\n",rq[i].ans); 
	return 0;
}

posted @   copper_carbonate  阅读(109)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示