牛客多校第八场E Enigmatic Partition(找规律+二次差分)

avatar

解法:

avatar

如上图,可以发现长度为7,最小数为1的序列对答案的贡献是有规律的,这种规律可以由5个区间加表示。

因为是区间加,可以采用差分数组来实现,但是有多个区间加,如果每次枚举的长度和最小数都要O(n)时间来维护,那么结果必然是TLE。因为这几个区间是有规律的,即左端点每次+2,右端点每次+1,可以考虑对差分数组建立差分数组来O(1)实现多个区间加。

可以考虑分别建立add数组和del数组,add数组只考虑区间的左端点,del数组只考虑区间的右端点。最后合并add数组和del数组就是f(i)差分数组了。

假设枚举的区间长度为len,最小数字为base,则最左边区间的左端点left=base*len+3,因此可以对add和del的差分数组(此处是2次差分)进行如下操作,即可维护一系列的区间和操作:

add[left]++;add[left+len-2+len-3+1]--;
del[left+len-2]++;del[left+len-2+len-3+1]--;
//为什么右端点都是left+len-2+len-3+1?因为一次差分右端点要右移1,二次差分就要右移2,对应图上的右端点的就是20

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn=1e5+5;
ll add[maxn*3],del[maxn*3],sum[maxn*3];
int main () {
    const ll n=1e5;
    //首先得到add的隔2差分序列和del的隔1差分序列
    for(ll len=3;len<=n;len++){
        for(ll base=1;base*len<=n;base++){
            ll left=len*base+3;
            add[left]++;add[left+len-2+len-3+1]--;
            del[left+len-2]++;del[left+len-2+len-3+1]--;
        }
    }
    //然后得到差分序列(add序列和del序列)
    for(ll i=3;i<=n;i++){
        add[i]+=add[i-2];
        del[i]+=del[i-1];
    }
    //合并差分序列
    for(int i=3;i<=n;i++){
        add[i]-=del[i];
    }
    //最后得到原序列
    for(ll i=3;i<=n;i++){
        add[i]+=add[i-1];
    }
    //得到前缀和序列
    for(ll i=3;i<=n;i++){
        sum[i]=sum[i-1]+add[i];
    }
    ll T;
    scanf("%lld",&T);
    for(ll kase=1;kase<=T;kase++){
        ll l,r;
        scanf("%lld%lld",&l,&r);
        printf("Case #%lld: %lld\n",kase,sum[r]-sum[l-1]);
    }
}

参考博客: https://blog.csdn.net/tianyizhicheng/article/details/107773762

posted @ 2020-08-04 10:54  UCPRER  阅读(340)  评论(0编辑  收藏  举报