[多校联考2021] 模拟赛1

总结

对于以前讲过的题还是要复习吧,比如今天 \(\tt T3\) 就是正睿讲的那个题,但是不会做了。

考场上 \(A\)\(\tt T2\) 挺不错,只是语言选错了拿了暴力分

思维不行真的没办法,但是能拿的分都要拿到吧。

盗梦空间

题目描述

\(q\) 次询问,每次给定树上 \(k\) 个关键点,求所有点中距离关键点最近距离的最大值。

\(n,m,q\leq 100000\)

解法

对于每次询问建虚树,然后讨论一下答案点落在哪里。

  • 落在虚树结点上:树形DP解决。
  • 落在空子树内:预处理子树内最远点,将儿子按照该值排序。
  • 在虚树边上:二分边上的分界点在哪里,预处理倍增数组查询答案。

这东西除了 \(\tt Oneindark\) 谁写得出来啊!

爱乐之城

题目描述

要不你给我谨慎点,要不你给我常数小点。

提交的时候没选 \(\tt O_2\),直接 \(\tt T\) 飞了。嘤嘤嘤

给定 \(n\) 个元素 \(a_{1..n}\),对于 \(i\in[1,n]\),求 \(\forall i\in[1,n],F(\{a_{1..i}\})\),其中:

\[zxy_1(n)=\sum_{i=1}^ni\sum_{j=1}^n[(i,j)=1]j \]

\[zxy_2(n)=\sum_{i=1}^n\sum_{j=1}^n\mu(ij) \]

\[F(S)=\sum_{T\in S}zxy_2(\gcd(a\in T))\prod_{a\in T}zxy_1(a) \]

答案对 \(998244353\) 取模,强制在线,\(1\leq n,a_i\leq 4e5\)

解法

你要知道其实是三个不相关的子问题。

对于子问题 \(1\),也就是求这个:

\[zxy_1(a_i)=\sum_{j=1}^{a_i}j\sum_{k=1}^{a_i}[(j,k)=1]\cdot k \]

这个只能在线算,把里面那东西反演一下:

\[\sum_{j=1}^{a_i}j\sum_{k=1}^{a_i}k\sum_{x|(j,k)}\mu(x) \]

\[\sum_{x=1}^{a_i}\mu(x)\cdot x^2\cdot sum(\frac{a_i}{x})^2 \]

其中 \(sum(i)\) 表示 \(1+2+....i\),这个数论分块可以直接 \(O(\sqrt m)\),应该是正确的复杂度。


对于子问题 \(2\),记 \(d=\gcd(a_1,a_2...,a_i)\),也就是求这个:

\[zxy_2(d)=\sum_{i=1}^d\sum_{j=1}^d\mu(ij) \]

额,这个东西可以 \(O(m^2)\) 预处理,期望得分 \(35\) 分。

因为是 \(\tt gcd\),而且 \(a_1\) 是知道的,所以他是 \(a_1\) 的因数,有 \(\sqrt m\) 种取值,如果对于每一种取值能够快速算就好了,会不会 \(ij\) 对数很小呢?别想了,已经试过了,数量级大的一批。

因为不能把 \(\mu\) 筛都不能筛出来,分析一下他的性质,有了,我们先加一些条件再积性函数分拆。

An idea strikes me.

\[\sum_{i=1}^d\sum_{j=1}^d[(i,j)=1]\mu(ij) \]

\[\sum_{i=1}^d\mu(i)\sum_{j=1}^d\mu(j)\sum_{x|(i,j)}\mu(x) \]

\[\sum_{x=1}^{d}\mu(x)\cdot zy(x)^2 \]

其中 \(zy(x)=\sum_{x|i}\mu(i)\),这个东西很容易预处理,额 \(...\) 所以我们在线算 \(zy(x)\) 不就行了?


凉啦,求的是所有情况的答案,\(100->0\),先不做这题了。

这可能就是命运吧。

不管了我就是要硬刚这个题:

\[\sum_{T\in S}\Big(\prod_{x}zxy_1(x)\Big)\cdot zxy_2(d) \]

\[\sum_{d=1}^{m}zxy_2(d)\sum_{T\in S}\Big(\prod_{x\in T}zxy_1(x)\cdot [d|x]\Big)(\gcd(x_i/d)=1) \]

\[\sum_{d=1}^mzxy_2(d)\Big(\prod_{x\in S}(zxy_1(x)\cdot [d|x]+1)\Big)\sum_{z|\gcd(x_i/d)}\mu(z) \]

\[\sum_{d=1}^mzxy_2(d)\sum_{z=1}^{m/d}\mu(z)\Big(\prod_{x\in S}zxy_1(x)\cdot [dz|x]+1\Big) \]

\[\sum_{T=1}^m\sum_{z|T}\mu(z)\cdot zxy_2(\frac{T}{z}).... \]

前面那个东西是系数,可以直接算出来。

后面那东西好像可以边做边维护诶,每次就把 \(a_i\) 拿去分解就行了呗,把因数的答案更新下。

但是算 \(zxy_2(d)\) 的时间复杂度又爆掉了啊,现在要 \(O(m^2)\) 来算了,对了,那个东西在 \(zy(x)\) 改的时候顺便修改一下不就行了吗?

时间复杂度 \(O(m\sqrt m)\),真不错!但是好像还是过不了。


第一个子问题可以搞,我们直接把所有 \(v\) 的都处理出来,还是一个一个增加。

可以考虑成有若干个 \(x\),如果碰到一个 \(x\) 的倍数那么会改,所以也可以 \(O(m\log m)\)

都用因数的方式来考虑,都可以 \(O(m\log m)\)

#include <cstdio>
#include <vector>
using namespace std;
const int M = 400005;
const int MOD = 998244353;
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x<=9) {putchar(x+'0');return ;}
	write(x/10);putchar(x%10+'0');
}
int n,m,op,cnt,ans,sy,a[M],b[M],c[M],f[M],g[M],zxy[M],now[M];
int cur,mu[M],p[M],vis[M],zy[M],sum[M];vector<int> v[M];
void sieve(int n)
{
	mu[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!vis[i])
		{
			mu[i]=-1;
			p[++cnt]=i;
		}
		for(int j=1;j<=cnt && i*p[j]<=n;j++)
		{
			vis[i*p[j]]=1;
			if(i%p[j]==0) break;
			mu[i*p[j]]=-mu[i];
		}
	}
	for(int i=1;i<=n;i++)
	{
		b[i]=mu[i]*i*i%MOD; 
		c[i]=(c[i-1]+i)%MOD;
	}
	for(int i=1;i<=n;i++)
		c[i]=c[i]*c[i]%MOD;
}
int gcd(int a,int b)
{
	return !b?a:gcd(b,a%b);
}
void upd(int x,int y)
{
	cur=(cur-mu[x]*zy[x]*zy[x])%MOD;
	zy[x]+=y;
	cur=(cur+mu[x]*zy[x]*zy[x])%MOD;
}
void fuck(int x,int y)
{
	ans=(ans-g[x]*f[x])%MOD;
	f[x]=f[x]*(y+1)%MOD;
	ans=(ans+g[x]*f[x])%MOD;
}
void add(int x)
{
	sy=(sy-b[x]*c[now[x]])%MOD;
	now[x]++;
	sy=(sy+b[x]*c[now[x]])%MOD;
}
signed main()
{
	freopen("lalaland.in","r",stdin);
	freopen("lalaland.out","w",stdout); 
	n=read();m=read();op=read();
	sieve(m);
	for(int i=1;i<=n;i++)
		a[i]=read();
	//把每个数的因数预处理出来
	for(int i=1;i<=m;i++)
		for(int j=i;j<=m;j+=i)
			v[j].push_back(i);
	for(int i=1;i<=m;i++)
	{
		//这东西要在线算 
		for(int j=0;j<v[i].size();j++)
			upd(v[i][j],mu[i]);
		sum[i]=cur;f[i]=1;
	}
	for(int i=1;i<=m;i++)
		for(int j=i;j<=m;j+=i)
			g[j]=(g[j]+mu[i]*sum[j/i])%MOD; 
	//优化 
	for(int i=1;i<=m;i++)
	{
		for(int j=0;j<v[i].size();j++)
			add(v[i][j]);
		zxy[i]=sy;
	}
	//在线算呗
	for(int i=1;i<=n;i++)
	{
		if(op==1) a[i]=(19891989*ans+a[i])%m+1;
		//出题人死了几个吗这么搞,WDNMD!
		//枚举a[i]的所有因数 
		for(int j=0;j<v[a[i]].size();j++)
			fuck(v[a[i]][j],zxy[a[i]]);
		ans=(ans+MOD)%MOD;
		if(op==0 && i==n) printf("%lld\n",ans);
		else if(op==1) printf("%lld\n",ans);
	}
}

星际穿越

题目描述

Once you’re a parent, you’re the ghost of your children’s future.

And love is the only thing we’re capable of perceiving that transcends time and space.

求满足下列条件的 \(r\times n\) 的矩阵个数:

  • 给定一个正整数 \(k\),我们说 \(j(1\leq j<n)\) 是稳定的,当且仅当 \(\forall 1\leq i\leq r,a_{i,j}<a_{i,j+1}\)
  • 每一行是一个长度为 \(n\) 的排列
  • \(j\)\(k\) 的倍数,则第 \(j\) 列应该是不稳定的,否则若 \(j\) 不是 \(k\) 的倍数,则 \(j\) 列应该是稳定的。

答案对 \(998244353\) 取模。

\(n\leq 1000000,r\leq 19891989\)

解法

先讲一个 \(40\) 分的针对 \(r=1,n\leq2000\) 的做法,不会真的有人去打五分的爆搜吧

就用类似连续段 \(dp\) 的思想,每次插入一个新的数就会带来一些改变,也就是 \(dp\) 过程中排列是不确定的。所以就不能存固定的数值,设 \(dp[i][j]\) 表示填完前 \(i\) 个位置,\(i\) 位置的数位前 \(i\) 个数中的第 \(j\) 大,转移就看当前位置是不是 \(k\) 的倍数,用前缀后缀和算一下就行了,时间复杂度 \(O(n^2)\)


正解需要容斥,但是我们是做过类似的题的:不等关系,令 \(m=\lfloor\frac{n-1}{k}\rfloor\)\(ans_i\) 表示有 \(i\) 个位置违反的方案数,那么:

\[Ans=\sum_{j=0}^m(-1)^jans_j \]

考虑 \(dp\) 维护容斥系数,设 \(f_i\) 表示考虑到 \(i\) 的容斥系数,转移就枚举连续的一段放小于号,段的端点一定是不稳定的大于号,但是我们让他是随便放的,所以就会划分出段。对于那些被钦定成了小于号的大于号,会贡献一个 \(-1\) 的系数,我们再最后乘上 \((-1)^m\) 在随便放的那里贡献 \(-1\)(方便转移):

\[f_i=-\sum_{j=0}^{i-1}\frac{f_j}{[k(i-j)]!} \]

除以 \([k(i-j)]!\) 表示可重集的排列数,因为段内顺序一定,全局又是无序的,那么最后的答案是:

\[n!\cdot(-1)^m\sum_{i=0}^m\frac{f_i}{(n-ik)!} \]

上面讨论的是 \(r=1\) 的情况,你发现转移算的是一段小于号的情况,这个可以直接扩展到 \(r>1\) 的情况,因为这样每一行就是独立的了,直接乘法原理即可,直接把所有的阶乘快速幂一下。

暴力算这个 \(dp\)\(O(n^2)\) 的,但是可以分治 \(\tt FFT\) 可以 \(O(n\log^2n)\)

众所周知,简单的分治 \(\tt FFT\) 可以用多项式求逆优化,设 \(F(x)=\sum_{j=1}^\infty f_ix^i,G(x)=\sum_{j=1}^\infty\frac{1}{(jk)!}\)

\[F(x)=-(F(x)+1)G(x) \]

\[F(x)=\frac{-G(x)}{1+G(x)} \]

直接多项式求逆做到 \(O(n\log n)\)

#include <cstdio>
#include <iostream>
using namespace std;
const int M = 4000005;
const int MOD = 998244353;
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,r,k,ans,fac[M],inv[M],a[M],f[M],g[M];
namespace poly
{
	int len,A[M],B[M],rev[M];
	int qkpow(int a,int b)
	{
		int r=1;
		while(b>0)
		{
			if(b&1) r=r*a%MOD;
			a=a*a%MOD;
			b>>=1;
		}
		return r;
	}
	void NTT(int *a,int len,int op)
	{
		for(int i=0;i<len;i++)
		{
			rev[i]=(rev[i>>1]>>1)|((i&1)*(len/2));
			if(i<rev[i]) swap(a[i],a[rev[i]]);
		}
		for(int s=2;s<=len;s<<=1)
		{
			int t=s/2,w=(op==1)?qkpow(3,(MOD-1)/s):qkpow(3,MOD-1-(MOD-1)/s);
			for(int i=0;i<len;i+=s)
				for(int j=0,x=1;j<t;j++,x=x*w%MOD)
				{
					int fe=a[i+j],fo=a[i+j+t];
					a[i+j]=(fe+x*fo)%MOD;
					a[i+j+t]=((fe-x*fo)%MOD+MOD)%MOD;
				}
		}
		if(op==1) return ;
		int inv=qkpow(len,MOD-2);
		for(int i=0;i<len;i++) a[i]=a[i]*inv%MOD;
	}
	void work(int n,int *a,int *b)
	{
		len=1;while(len<2*n) len<<=1;
		for(int i=0;i<len;i++) A[i]=B[i]=0;
		for(int i=0;i<n;i++) A[i]=a[i];
		for(int i=0;i<n/2;i++) B[i]=b[i];
		NTT(A,len,1);NTT(B,len,1);
		for(int i=0;i<len;i++)
			A[i]=((2*B[i]-B[i]*B[i]%MOD*A[i])%MOD+MOD)%MOD;
		NTT(A,len,-1);
		for(int i=0;i<n;i++) b[i]=A[i];
	}
	void inv(int n,int *a,int *b)
	{
		b[0]=qkpow(a[0],MOD-2);
		int cur=1;
		while(cur<n)
		{
			cur<<=1;
			work(cur,a,b);
		}
	}
	void mul(int n,int *a,int *b)
	{
		len=1;while(len<2*n) len<<=1;
		for(int i=0;i<len;i++) A[i]=B[i]=0;
		for(int i=0;i<n;i++) A[i]=a[i],B[i]=b[i];
		NTT(A,len,1);NTT(B,len,1);
		for(int i=0;i<len;i++) A[i]=A[i]*B[i]%MOD;
		NTT(A,len,-1);
		//只需要n项 
		for(int i=0;i<n;i++) b[i]=A[i];
	}
};
void init(int n)
{
	fac[0]=inv[0]=inv[1]=1;
	for(int i=2;i<=n;i++) inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD;
	for(int i=2;i<=n;i++) inv[i]=inv[i-1]*inv[i]%MOD;
	for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%MOD;
	for(int i=1;i<=n;i++)
	{
		inv[i]=poly::qkpow(inv[i],r);
		fac[i]=poly::qkpow(fac[i],r);
	}
}
signed main()
{
	freopen("interstellar.in","r",stdin);
	freopen("interstellar.out","w",stdout);
	n=read();r=read();k=read();m=(n-1)/k+1;
	init(1e6);
	for(int i=1;i<m;i++)
		g[i]=MOD-inv[i*k],a[i]=inv[i*k];
	a[0]=1;
	poly::inv(m,a,f);
	poly::mul(m,g,f);
	f[0]=1;//这里别忘记了哦 
	for(int i=0;i<m;i++)
		ans=(ans+f[i]*inv[n-i*k])%MOD;
	if((m-1)&1) ans=MOD-ans;
	ans=ans*fac[n]%MOD;
	printf("%lld\n",ans);
}
posted @ 2021-03-30 19:16  C202044zxy  阅读(125)  评论(0编辑  收藏  举报