Codeforces 1550 D. Excellent Arrays —— 组合数,枚举

This way

题意:

称序列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))。此时会有一边有了固定的取法:
Codeforces 1550 D. Excellent Arrays —— 组合数,枚举
蓝线代表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还没有能让一半的位置固定,但是另一边也要固定了:
Codeforces 1550 D. Excellent Arrays —— 组合数,枚举
比如说像这样。

	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;
}

上一篇:洛谷 P6776 - [NOI2020] 超现实树(找性质,神仙题)


下一篇:codeforces 1557D Ezzat and Grid 线段树 DP