题目相关
题目链接
题目大意
给定
n
n
n,
q
q
q组询问,每组询问包含
x
,
y
x,y
x,y,求满足条件的排列
a
a
a数量
条件1:
m
a
x
(
a
1
,
a
2
⋅
⋅
⋅
a
y
)
=
a
y
max(a_1,a_2···a_y)=a_y
max(a1,a2⋅⋅⋅ay)=ay
条件2:
2
a
x
<
a
y
2a_x<a_y
2ax<ay
数据范围
1 ≤ n , q ≤ 1000000 , 1 ≤ x < y ≤ n 1\le n,q\le1000000,1\le x<y\le n 1≤n,q≤1000000,1≤x<y≤n
题解
这题的
Θ
(
n
l
o
g
n
)
\Theta(nlogn)
Θ(nlogn)算法不能过
首先发现,
x
x
x的值无论如何变化答案相同(可以把
x
x
x位看成特殊位)
我们发现直接求并不好做,我们考虑这样一件事情:
如果题目的条件2改为
2
a
x
>
a
y
2a_x>a_y
2ax>ay,那么答案不变
为什么?
我们发现,当
a
y
a_y
ay确定的时候,对于满足
k
<
a
y
k<a_y
k<ay的数,满足
2
k
<
a
y
2k<a_y
2k<ay和满足
2
k
>
a
y
2k>a_y
2k>ay的数是一样多的,所以答案也是一样的
设
A
l
e
s
s
A_{less}
Aless为条件2为
2
a
x
<
a
y
2a_x<a_y
2ax<ay时的答案(即我们要求的),
设
A
m
o
r
e
A_{more}
Amore为条件2为
2
a
x
>
a
y
2a_x>a_y
2ax>ay时的答案,
设
A
e
q
u
a
l
A_{equal}
Aequal为条件2为
2
a
x
=
a
y
2a_x=a_y
2ax=ay时的答案,
设
A
n
o
n
e
A_{none}
Anone为条件2没有时的答案,
容易发现
A
n
o
n
e
=
(
n
y
)
(
y
−
1
)
!
(
n
−
y
)
!
=
n
!
y
!
(
n
−
y
)
!
(
y
−
1
)
!
(
n
−
y
)
!
=
n
!
y
A_{none}=\binom{n}{y}(y-1)!(n-y)!=\frac{n!}{y!(n-y)!}(y-1)!(n-y)!=\frac{n!}y
Anone=(yn)(y−1)!(n−y)!=y!(n−y)!n!(y−1)!(n−y)!=yn!
(即取出
y
y
y个数,把最大值作为
a
y
a_y
ay,再枚举前后的摆放方式,当然也可以用其它方法推)
并且
A
l
e
s
s
+
A
m
o
r
e
+
A
e
q
u
a
l
=
A
n
o
n
e
A_{less}+A_{more}+A_{equal}=A_{none}
Aless+Amore+Aequal=Anone
由于我们知道
A
l
e
s
s
=
A
m
o
r
e
A_{less}=A_{more}
Aless=Amore
所以
A
l
e
s
s
=
A
n
o
n
e
−
A
e
q
u
a
l
2
A_{less}=\frac{A_{none}-A_{equal}}{2}
Aless=2Anone−Aequal
考虑如何求
A
e
q
u
a
l
A_{equal}
Aequal
设
g
i
g_i
gi为
y
=
i
y=i
y=i时
a
1
⋅
⋅
⋅
a
y
a_1···a_y
a1⋅⋅⋅ay的选择方案数
容易发现
A
e
q
u
a
l
=
g
y
(
y
−
2
)
!
(
n
−
y
)
!
A_{equal}=g_y(y-2)!(n-y)!
Aequal=gy(y−2)!(n−y)!
考虑怎么求
g
i
g_i
gi
g
i
=
∑
j
=
1
⌊
n
2
⌋
(
2
j
−
2
i
−
2
)
=
∑
j
=
0
⌊
n
2
⌋
−
1
(
2
j
i
)
\begin{aligned} g_i&=\sum_{j=1}^{\left\lfloor \frac n2\right\rfloor}\binom{2j-2}{i-2}\\ &=\sum_{j=0}^{\left\lfloor \frac n2\right\rfloor-1}\binom{2j}{i} \end{aligned}
gi=j=1∑⌊2n⌋(i−22j−2)=j=0∑⌊2n⌋−1(i2j)
我们将其一般化
假设给定
m
m
m
现在要求的是
f
n
=
∑
i
=
0
m
(
2
i
n
)
f_n=\sum_{i=0}^m\binom{2i}{n}
fn=i=0∑m(n2i)
根据经典性质
(
n
m
)
=
(
n
−
1
m
)
+
(
n
−
1
m
−
1
)
\binom nm=\binom{n-1}m+\binom{n-1}{m-1}
(mn)=(mn−1)+(m−1n−1)
我们可以将
f
f
f进行一些转化
f
n
=
∑
i
=
0
m
(
2
i
n
)
=
∑
i
=
0
m
(
(
2
i
−
1
n
−
1
)
+
(
2
i
−
1
n
)
)
=
∑
i
=
0
m
(
2
i
−
1
n
−
1
)
+
∑
i
=
0
m
(
2
i
−
1
n
)
\begin{aligned} f_n&=\sum_{i=0}^m\binom{2i}{n}\\ &=\sum_{i=0}^m(\binom{2i-1}{n-1}+\binom{2i-1}{n})\\ &=\sum_{i=0}^m\binom{2i-1}{n-1}+\sum_{i=0}^m\binom{2i-1}{n}\\ \end{aligned}
fn=i=0∑m(n2i)=i=0∑m((n−12i−1)+(n2i−1))=i=0∑m(n−12i−1)+i=0∑m(n2i−1)
列出组合数的一个经典式子(我们发现这个式子和我们要求的
f
f
f非常像)
∑
i
=
x
y
(
i
x
)
=
(
y
+
1
x
+
1
)
\sum_{i=x}^y\binom{i}{x}=\binom{y+1}{x+1}
i=x∑y(xi)=(x+1y+1)
将两个
f
n
f_n
fn相加
2
f
n
=
∑
i
=
0
m
(
2
i
−
1
n
−
1
)
+
∑
i
=
0
m
(
2
i
−
1
n
)
+
∑
i
=
0
m
(
2
i
n
)
=
∑
i
=
0
m
(
2
i
−
1
n
−
1
)
+
∑
i
=
0
m
(
2
i
−
1
n
)
+
∑
i
=
0
m
(
2
i
n
)
+
f
n
−
1
−
f
n
−
1
=
∑
i
=
0
m
(
2
i
−
1
n
−
1
)
+
∑
i
=
0
m
(
2
i
n
−
1
)
+
∑
i
=
0
m
(
2
i
−
1
n
)
+
∑
i
=
0
m
(
2
i
n
)
−
f
n
−
1
=
∑
i
=
0
2
m
(
i
n
−
1
)
+
∑
i
=
0
2
m
(
i
n
)
−
f
n
−
1
=
(
2
m
+
1
n
)
+
(
2
m
+
1
n
+
1
)
−
f
n
−
1
=
(
2
m
+
2
n
+
1
)
−
f
n
−
1
\begin{aligned} 2f_n&=\sum_{i=0}^m\binom{2i-1}{n-1}+\sum_{i=0}^m\binom{2i-1}{n}+\sum_{i=0}^m\binom{2i}{n}\\ &=\sum_{i=0}^m\binom{2i-1}{n-1}+\sum_{i=0}^m\binom{2i-1}{n}+\sum_{i=0}^m\binom{2i}{n}+f_{n-1}-f_{n-1}\\ &=\sum_{i=0}^m\binom{2i-1}{n-1}+\sum_{i=0}^m\binom{2i}{n-1}+\sum_{i=0}^m\binom{2i-1}{n}+\sum_{i=0}^m\binom{2i}{n}-f_{n-1}\\ &=\sum_{i=0}^{2m}\binom i{n-1}+\sum_{i=0}^{2m}\binom i{n}-f_{n-1}\\ &=\binom{2m+1}{n}+\binom{2m+1}{n+1}-f_{n-1}\\ &=\binom{2m+2}{n+1}-f_{n-1}\\ \end{aligned}
2fn=i=0∑m(n−12i−1)+i=0∑m(n2i−1)+i=0∑m(n2i)=i=0∑m(n−12i−1)+i=0∑m(n2i−1)+i=0∑m(n2i)+fn−1−fn−1=i=0∑m(n−12i−1)+i=0∑m(n−12i)+i=0∑m(n2i−1)+i=0∑m(n2i)−fn−1=i=0∑2m(n−1i)+i=0∑2m(ni)−fn−1=(n2m+1)+(n+12m+1)−fn−1=(n+12m+2)−fn−1
容易发现
m
=
⌊
n
2
⌋
−
1
m=\left\lfloor \frac n2\right\rfloor-1
m=⌊2n⌋−1
所以
g
x
=
(
2
⌊
n
2
⌋
x
+
1
)
−
g
x
−
1
2
g_x=\frac{\binom{2\left\lfloor \frac n2\right\rfloor}{x+1}-g_{x-1}}2
gx=2(x+12⌊2n⌋)−gx−1
直接递推即可,预处理组合数,算法总复杂度
Θ
(
n
)
\Theta(n)
Θ(n)
代码
贴上AC代码
#include<cstdio>
#include<cctype>
namespace fast_IO
{
const int IN_LEN=10000000,OUT_LEN=10000000;
char ibuf[IN_LEN],obuf[OUT_LEN],*ih=ibuf+IN_LEN,*oh=obuf,*lastin=ibuf+IN_LEN,*lastout=obuf+OUT_LEN-1;
inline char getchar_(){return (ih==lastin)&&(lastin=(ih=ibuf)+fread(ibuf,1,IN_LEN,stdin),ih==lastin)?EOF:*ih++;}
inline void putchar_(const char x){if(oh==lastout)fwrite(obuf,1,oh-obuf,stdout),oh=obuf;*oh++=x;}
inline void flush(){fwrite(obuf,1,oh-obuf,stdout);}
}
using namespace fast_IO;
#define getchar() getchar_()
#define putchar(x) putchar_((x))
typedef long long ll;
#define rg register
template <typename T> inline T max(const T a,const T b){return a>b?a:b;}
template <typename T> inline T min(const T a,const T b){return a<b?a:b;}
template <typename T> inline void mind(T&a,const T b){a=a<b?a:b;}
template <typename T> inline void maxd(T&a,const T b){a=a>b?a:b;}
template <typename T> inline T abs(const T a){return a>0?a:-a;}
template <typename T> inline void swap(T&a,T&b){T c=a;a=b;b=c;}
template <typename T> inline void swap(T*a,T*b){T c=a;a=b;b=c;}
template <typename T> inline T gcd(const T a,const T b){if(!b)return a;return gcd(b,a%b);}
template <typename T> inline T square(const T x){return x*x;};
template <typename T> inline void read(T&x)
{
char cu=getchar();x=0;bool fla=0;
while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}
while(isdigit(cu))x=x*10+cu-'0',cu=getchar();
if(fla)x=-x;
}
template <typename T> void printe(const T x)
{
if(x>=10)printe(x/10);
putchar(x%10+'0');
}
template <typename T> inline void print(const T x)
{
if(x<0)putchar('-'),printe(-x);
else printe(x);
}
const int maxn=1000001,mod=998244353;
int fac[maxn],ifac[maxn],inv[maxn];
inline int pow(int x,int y)
{
int res=1;
for(;y;y>>=1,x=(ll)x*x%mod)if(y&1)res=(ll)x*res%mod;
return res;
}
inline int C(const int x,const int y)
{
return (ll)fac[x]*ifac[y]%mod*ifac[x-y]%mod;
}
int n,q,f[maxn];
int main()
{
fac[0]=1;
for(rg int i=1;i<=1000000;i++)fac[i]=(ll)fac[i-1]*i%mod;
ifac[1000000]=pow(fac[1000000],mod-2);
inv[0]=1;
for(rg int i=1000000;i>=1;i--)ifac[i-1]=(ll)ifac[i]*i%mod,inv[i]=(ll)ifac[i]*fac[i-1]%mod;
read(n),read(q);
const int INV=inv[2];
f[0]=n>>1;
const int P=(n>>1)<<1;
for(rg int i=1;i<=n;i++)f[i]=(ll)INV*(C(P,i+1)+mod-f[i-1])%mod;
while(q--)
{
int x,y;read(x),read(y);
print(((ll)fac[n]*inv[y]+mod-(ll)f[y-2]*fac[n-y]%mod*fac[y-2]%mod)%mod*INV%mod),putchar('\n');
}
return flush(),0;
}
总结
常数极小,稳稳的过
弱弱的推式子题(为何很多人都懒得自己推啊),转化问题很巧妙