100035. 【NOIP2017提高A组模拟7.10】区间

题目大意

给定一个长为n的数列S,对于一个给定的k,求出 t j 表示区间[j,j + k - 1]所有元素的乘积,你只需要把所有 t j 对P取模后,输出它们的异或和.
给定A,B,C,D,定义S的计算方法为 S 1 = A , S i = ( S i 1 B + C ) m o d D .

数据范围

对于20%的数据: n <= 1000
对于50%的数据: n <= 2 10 5
另有20%的数据: n <= 2 10 6 , n k <= 10
对于100%的数据: 1 <= k <= n <= 2 10 7 , 0 <= A , B , C < D <= 10 9 , 1 <= P <= 10 9
在所有数据中均匀分布50%的数据满足P是质数,这50%中50%满足 P <= 10 7

Solutions

50%:注意 n <= 2 10 5 可以使用线段树维护乘积
时间复杂度:O( ( n k + 1 ) l o g ( n k + 1 ) -> n l o g n )
另有20%: n k <= 10 直接暴力即可
100%:将序列分为每块长度为k的块,对于每一块维护一个前缀积和后缀积,对于每个询问利用前缀积和后缀积求出即可.
时间复杂度:O( n )

50%:

#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 2e6 + 5;

struct node
{
    long long val;
}tr[N << 2];

int n,k,P,A,B,C,D;
long long ans,ret;
long long S[N * 10];

void build(int rt,int l,int r)
{
    if (l == r) 
    {
        tr[rt].val = S[l];
        return;
    }
    int mid = l + r >> 1;
    build(rt << 1,l,mid);
    build(rt << 1 | 1,mid + 1,r);
    tr[rt].val = tr[rt << 1].val * tr[rt << 1 | 1].val % P;
}

long long query(int rt,int l,int r,int l_,int r_)
{
    if (l > r_ || r < l_) return 1;
    if (l_ <= l && r_ >= r) return tr[rt].val;
    int mid = l + r >> 1;
    return query(rt << 1,l,mid,l_,r_) * query(rt << 1 | 1,mid + 1,r,l_,r_) % P;
}

int main()
{
    freopen("range.in","r",stdin);
    freopen("range.out","w",stdout);
    scanf("%d%d%d",&n,&k,&P);
    scanf("%d%d%d%d",&A,&B,&C,&D);
    S[1] = A;
    for (int i = 2 ; i <= n ; i++) 
    {
        long long p = S[i - 1] * B;
        if (p) p = p % D + C; else p = C;
        if (p) S[i] = p % D; else S[i] = 0;
    }
    if (n - k <= 10)
    {
        for (int i = 1,j = k ; i <= n - k + 1 ; i++,j++)
        {
            ret = 1;
            for (int l = i ; l <= j ; l++) ret = ret * S[l] % P;
            ans = ans ^ ret;
        }
        if (ans) ans = ans % P;
        printf("%lld\n",ans);
        return 0;
    }
    build(1,1,n);
    for (int i = 1,j = k ; i <= n - k + 1 ; i++,j++)
    {
        long long t = query(1,1,n,i,j);
        if (t) t = t % P;
        ans ^= t;
    }
    if (ans) ans = ans % P;
    printf("%lld\n",ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

100%:

#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 2e7 + 5;

int n,k,P,tot,A,B,C,D;
int pre[N],suf[N],S[N];
long long ret,ans;

int main()
{
    freopen("range.in","r",stdin);
    freopen("range.out","w",stdout);
    scanf("%d%d%d",&n,&k,&P);
    scanf("%d%d%d%d",&A,&B,&C,&D);
    S[1] = A;
    for (int i = 2 ; i <= n ; i++) 
    {
        ret = (S[i - 1] * 1LL * B + C) % D;
        S[i] = ret;
    }
    for (int w = 1 ; w <= n ; w += k)
    {
        pre[w] = S[w];
        for (int j = w + 1 ; j <= min(w + k - 1,n) ; j++)
        {
            ret = pre[j - 1] * 1LL * S[j] % P; 
            pre[j] = ret;
        }
        suf[min(w + k - 1,n)] = S[min(w + k - 1,n)];
        for (int j = min(w + k - 1,n) - 1 ; j >= w ; j--)
        {
            ret = suf[j + 1] * 1LL * S[j] % P;
            suf[j] = ret;
        }
    }
    for (int i = 1 ; i <= n - k + 1 ; i++)
    {
        if (pre[i] == S[i]) ret = suf[i] % P;
        else ret = suf[i] * 1LL * pre[i + k - 1] % P;
        ans ^= ret;
    }
    printf("%lld\n",ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/P_hillipe/article/details/81607575