UR #19

前进四

题目描述

点此看题

解法

翻译一下,题目就是让你求从后往前的极长下降子序列的长度,可以直接上 \(O(n\log^2 n)\) 的板子。

这种优秀的做法在本题的环境下还是行不通,我们原来是依次扫描询问,维护每个位置的数据结构。我们换一种思路,依次从后往前依次扫描位置,维护每个询问的数据结构。

我们把所有东西离线下来,那么修改变成了对一段询问区间取 \(\min\),询问变成了对于某个询问单点求 \(\min\) 值的变化次数。

可以直接用势能线段树维护,在修改的时候顺便记一个变化次数的标记即可,时间复杂度 \(O(n\log n)\)

总结

神奇的离线方式增加了!以离线的方法来达到切换主体的目的。

#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
using namespace std;
const int M = 1000005;
const int N = M<<2;
#define pb push_back
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,ans[M],s[N],mx[N],cx[N];
vector<pair<int,int> > a[M];vector<int> q[M];
void down(int i)
{
	if(s[i])
	{
		if(mx[i]<mx[i<<1])
			mx[i<<1]=mx[i],s[i<<1]+=s[i];
		if(mx[i]<mx[i<<1|1])
			mx[i<<1|1]=mx[i],s[i<<1|1]+=s[i];
		s[i]=0;
	}
}
void up(int i)
{
	mx[i]=max(mx[i<<1],mx[i<<1|1]);
	cx[i]=max(cx[i<<1],cx[i<<1|1]);
	if(mx[i]!=mx[i<<1]) cx[i]=max(cx[i],mx[i<<1]);
	if(mx[i]!=mx[i<<1|1]) cx[i]=max(cx[i],mx[i<<1|1]);
}
void ins(int i,int l,int r,int L,int R,int c)
{
	if(L>r || l>R || mx[i]<=c) return ;
	if(L<=l && r<=R && cx[i]<c)
	{
		s[i]++;mx[i]=c;
		return ;
	}
	int mid=(l+r)>>1;down(i);
	ins(i<<1,l,mid,L,R,c);
	ins(i<<1|1,mid+1,r,L,R,c);
	up(i);
}
int ask(int i,int l,int r,int x)
{
	if(l==r) return s[i];
	int mid=(l+r)>>1;down(i);
	if(mid>=x) return ask(i<<1,l,mid,x);
	return ask(i<<1|1,mid+1,r,x);
}
signed main()
{
	n=read();m=read();
	for(int i=1;i<=n;i++) a[i].pb({0,read()});
	for(int i=1;i<=m;i++)
	{
		int op=read(),x=read();
		if(op==1) a[x].pb({i,read()});
		else q[x].pb(i);
	}
	for(int i=1;i<=n;i++) a[i].pb({m,0});
	memset(mx,0x3f,sizeof mx);
	memset(cx,-0x3f,sizeof cx);
	for(int i=n;i>=1;i--)
	{
		for(int j=0;j+1<a[i].size();j++)
			ins(1,1,m,a[i][j].first+1,
			a[i][j+1].first,a[i][j].second);
		for(int x:q[i]) ans[x]=ask(1,1,m,x);
	}
	for(int i=1;i<=m;i++)
		if(ans[i]) printf("%d\n",ans[i]);
}

通用测评号

题目描述

点此看题

一共有 \(n\) 只鸽子,小 R 每秒会等概率选择一只没有吃撑的鸽子并给他一粒玉米。一只鸽子饱了当且仅当它吃了的玉米粒数量 \(\geq b\),一只鸽子吃撑了当且仅当它吃了的玉米粒数量 \(\geq a\)

小 R 想要你告诉他,当所有鸽子都吃饱时,期望有多少只鸽子吃撑了。

解法

考虑贡献法,计算每个鸽子吃撑的贡献。由于这 \(n\) 只鸽子是全等的,所以只需要计算其他鸽子吃饱时,第一只鸽子已经吃撑的概率,那么这个概率乘上 \(n\) 就是答案。

考虑切换主体,所求概率等价于第一只鸽子吃撑时,存在鸽子还没吃饱的概率。此时的要求时没有吃饱的鸽子个数 \(\geq 1\),可以钦定没有吃饱的鸽子个数为 \(i\),会带来 \((-1)^{i}\) 的容斥系数:

\[ans=n\cdot \sum_{i=1}^{n-1}{n-1\choose i}\cdot (-1)^{i}\cdot p_{i+1} \]

其中 \(p_m\) 表示鸽子总数为 \(m\),第 \(1\) 只鸽子已经吃撑了,其他 \(m-1\) 只鸽子还没有吃饱的概率。我们枚举喂的次数 \(i\),强制最后一次喂第 \(1\) 只鸽子,然后计算合法的序列方案数,每种方案对概率的贡献是 \(\frac{1}{m^i}\)

\[p_m=\sum_i (i-1)!\cdot [x^{i-1}](\frac{x^{a-1}}{(a-1)!}\cdot (1+\frac{x}{1!}+\frac{x^2}{2!}...\frac{x^{b-1}}{(b-1)})^{m-1}) \]

\(f(x)=(1+\frac{x}{1!}+\frac{x^2}{2!}...\frac{x^{b-1}}{(b-1)!})\),那么现在的主要问题是快速求解 \(f^{m-1}(x)\)

考虑到 \((e^x)'=e^x\),可以对 \(f^{m}(x)\) 这个 \(\tt EGF\) 求导来找递推式:

\[(f^m(x))'=m\cdot f^{m-1}(x)\cdot f'(x)=m\cdot (f^m(x)-\frac{x^{b-1}}{(b-1)!}\cdot f^{m-1}(x)) \]

对两边都是取 \([x^{i-1}]\) 得到:

\[i[x^i]f^m(x)=m\cdot ([x^{i-1}]f^m(x)-[x^{i-1}]\frac{x^{b-1}}{(b-1)!}\cdot f^{m-1}(x)) \]

发现 \([x^{i-1}]f^m(x)\)\([x^{i-1}]\frac{x^{b-1}}{(b-1)!}\cdot f^{m-1}(x))\) 都是已知信息,所以可以直接递推,时间复杂度 \(O(n^2\cdot b)\)

总结

快速求 \(\tt EGF\) 的幂次,求导推通项是重要套路。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int M = 255;
const int N = M*M;
#define int long long
const int MOD = 998244353;
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,a,b,ans,f[M][N],g[M][N],inv[N],fac[N],finv[N];
void init()
{
	inv[0]=inv[1]=fac[0]=finv[0]=1;
	for(int i=2;i<N;i++) inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
	for(int i=1;i<N;i++) finv[i]=finv[i-1]*inv[i]%MOD;
	for(int i=1;i<N;i++) fac[i]=fac[i-1]*i%MOD;
	f[0][0]=1;g[0][b-1]=finv[b-1];
	for(int i=1;i<=n;i++)
	{
		f[i][0]=1;g[i][b-1]=finv[b-1];
		for(int j=1;j<=i*(b-1);j++)
		{
			f[i][j]=(f[i][j-1]-g[i-1][j-1]+MOD)%MOD;
			f[i][j]=f[i][j]*i%MOD*inv[j]%MOD;
			g[i][j+b-1]=f[i][j]*finv[b-1]%MOD;
		}
	}
	for(int i=0;i<=n;i++)
	{
		memset(g[i],0,sizeof g[i]);
		for(int j=0;j<=i*(b-1);j++)
			g[i][j+a-1]=f[i][j]*finv[a-1]%MOD;
	}
}
int C(int n,int m)
{
	if(n<m || m<0) return 0;
	return fac[n]*finv[m]%MOD*finv[n-m]%MOD;
}
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;
}
int get(int m)
{
	int r=0,pw=qkpow(m,MOD-1-a);
	for(int i=a;i<=a+(m-1)*(b-1);i++)
	{
		r=(r+pw*g[m-1][i-1]%MOD*fac[i-1])%MOD;
		pw=pw*inv[m]%MOD;
	}
	return r;
}
signed main()
{
	n=read();a=read();b=read();init();
	for(int i=1;i<n;i++)
	{
		int t=C(n-1,i)*get(i+1)%MOD;
		if(i&1) ans=(ans+t)%MOD;
		else ans=(ans+MOD-t)%MOD;
	}
	printf("%lld\n",ans*n%MOD);
}
posted @ 2022-06-14 19:26  C202044zxy  阅读(145)  评论(0编辑  收藏  举报