题意:
称序列a好,当这个序列满足以下条件:
∀i a[i]!=i
令F(a)为a序列中i,j的对数满足a[i]+a[j]=i+j
称一个序列a非常棒,当序列a满足以下条件:
1.a序列好
2.∀i l<=a[i]<=r
3.F(a)是所有满足条件的a序列中的最大值
问你有多少a序列非常棒
题解:
首先我们可以知道,F(a)满足时,a[i]=i+x,a[j]=j-x需要得到满足。那么显然,F(a)最大时,对于a序列来说一半的位置是当前位置的下标+x的,一半位置是当前位置的下标-x的,此时会有
l
e
n
2
4
\frac{len^2}{4}
4len2(len表示序列的长度),奇数另算。可以证明这个想法是正确的,本文就不写了。那么len是否就是n这个需要搞清楚,一开始我以为是尺取找到所有满足条件的位置,那么就耽误了一些时间。
我们可以知道如果n>1的时候,位置1~
n
2
\frac{n}{2}
2n都可以+1,位置
n
2
\frac{n}{2}
2n+1~n都可以-1.那么至少有这种情况满足整个序列都是好的。因此只需要考虑整个序列而不是分段(我也不晓得为啥我会想到分段去做)
那么可以很容易地想到随着值的变化,每个位置的情况都是不一样的。假设mi=min(1-l,r-n),表示位置1向下取的最大值,位置n向上取的最大值,他们两个的最小值。当x在[1,mi]区间中,每个位置都可以+x或者-x,此时我们需要保证的就是两种情况的数量是一半一半(奇数特殊考虑)。因此这一段的答案是
c
(
n
,
n
/
2
)
∗
m
i
c(n,n/2)*mi%mod*(n%2?2:1)
c(n,n/2)∗mi
第二种就是当x>mi,但是x<=mx,(mx=max(1-l,r-n))。此时会有一边有了固定的取法:
蓝线代表x,当蓝线这么长的时候,最右端已经不能向上取了,因此需要固定向下取,但是最左端依旧可以向上或者向下,因此这一段需要枚举x的值。有人可能会问,值的范围不是1e9吗,怎么枚举。此时又要注意一个问题:随着x变成x+1,那么最右端需要固定向下取的点数也+1,最多到n/2+1的位置。因为我们需要向上和向下的点的数量都是一半一半。于是这一段的答案:
ans=(ans+c(n-i,n/2))%mod;
if(n%2)ans=(ans+c(n-i,n/2+1))%mod;
奇数特殊考虑。
接下来要注意一种情况:当x还没有能让一半的位置固定,但是另一边也要固定了:
比如说像这样。
if(n/2>mx-mi){
for(int i=mx-mi+1;i<=n/2;i++){
ans=(ans+c(n-i-(i-mx+mi),n/2-(i-mx+mi)))%mod;
if(n%2)ans=(ans+c(n-i-(i-mx+mi),n/2+1-(i-mx+mi)))%mod;
}
}
此时一边固定的数量为i,另一边固定的数量为i-mx+mi。
当固定的数量到了最中间以后,虽然每个位置不能选择加或者减了,但是在值的大小上还是可以继续增加的。top就表示我们刚才最后取到的值的大小。这里为什么不用跟mx和mi去比较,自己思考一下就知道了。同样,奇数偶数分开考虑。
这种情况又多,又是数学题最烦啦
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=1e9+7;
const int N=2e5+5;
ll fac[N],inv[N];
ll qpow(ll a,ll b){ll ans=1;for(;b;b>>=1,a=a*a%mod)if(b&1)ans=ans*a%mod;return ans;}
ll c(int n,int m){return fac[n]*inv[m]%mod*inv[n-m]%mod;}
int main()
{
fac[0]=inv[0]=1;
for(int i=1;i<N;i++)fac[i]=fac[i-1]*i%mod;
inv[N-1]=qpow(fac[N-1],mod-2);
for(int i=N-2;i;i--)inv[i]=inv[i+1]*(i+1)%mod;
//for(int i=1;i<N;i++)p[i]=p[i-1]*2%mod;
//printf("%lld\n",143922563*4%mod);
int t;
scanf("%d",&t);
while(t--){
int n,l,r;
scanf("%d%d%d",&n,&l,&r);
ll ans=0;
int mi=min(1-l,r-n),mx=max(1-l,r-n);
ans=c(n,n/2)*mi%mod*(n%2?2:1)%mod;
//if(n%2)ans=(ans+c(n,n/2+1)*mi)%mod;
for(int i=1;i<=min(mx-mi,n/2);i++){
ans=(ans+c(n-i,n/2))%mod;
if(n%2)ans=(ans+c(n-i,n/2+1))%mod;
}
if(n/2>mx-mi){
for(int i=mx-mi+1;i<=n/2;i++){
ans=(ans+c(n-i-(i-mx+mi),n/2-(i-mx+mi)))%mod;
if(n%2)ans=(ans+c(n-i-(i-mx+mi),n/2+1-(i-mx+mi)))%mod;
}
}
if(n%2){
int top=n/2+mi;
int v=min(r-top-n/2,n/2+1-top-l);
ans=(ans+max(0,v))%mod;
v=min(r-top-n/2-1,n/2+2-top-l);
ans=(ans+max(v,0))%mod;
}
else {
int top=n/2+mi;
int v=min(r-top-n/2,n/2+1-top-l);
ans=(ans+max(0,v))%mod;
}
printf("%lld\n",ans);
}
return 0;
}