bestcoder#22NPY and girls

离线处理加莫队算法,再就是找转移的规律,进行处理,莫队算法我的理解就是将查询分格化,用的上限复杂度是nsqrtn,莫队算法在处理左右区间查询时,有很大的作用。自己画画图会更容易理解。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<string>
#include<cmath>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn = 30010;
const int mod=  1000000007;
int T,nn,m,c[maxn],sum[maxn],sqrtn;
struct node
{
    int x,y,i,ans;
} n[maxn];
int cmp(node a,node b) //莫队算法
{
    if(a.y/sqrtn==b.y/sqrtn)
        return a.x/sqrtn<b.x/sqrtn;
    return a.y/sqrtn<b.y/sqrtn;
}
int comp(node a,node b)
{
    return a.i<b.i;
}
int exgcd(int a,int b, int &x,int &y) //ax+by=gcd(a,b)可用于求逆元
{
    int d=a;
    if(b!=0)
    {
        d=exgcd(b,a%b,y,x);
        y-=(a/b)*x;
    }
    else
    {
        x=1,y=0;
    }
    return d;
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d",&nn,&m);
        for(int i=1; i<=nn; i++)scanf("%d",&c[i]);
        sqrtn=(int)sqrt(nn);
        for(int i=0; i<m; i++)scanf("%d %d",&n[i].x,&n[i].y),n[i].i=i;
        sort(n,n+m,cmp);
        memset(sum,0,sizeof(sum));
        int x=1,y=0;
        LL ans=1;
        for(int i=0; i<m; i++)
        {
            while(y<n[i].y)
            {
                y++;
                sum[c[y]]++;
                int a,b;
                exgcd(sum[c[y]],mod,a,b);
                if(a<0)a+=mod;
                ans=(ans*(LL)(y-x+1)%mod)%mod;
                ans=(ans*(LL)a)%mod;
            }
            while(x>n[i].x)
            {
                x--;
                sum[c[x]]++;
                int a,b;
                exgcd(sum[c[x]],mod,a,b);
                if(a<0)a+=mod;
                ans=(ans*(LL)(y-x+1)%mod)%mod;
                ans=(ans*(LL)a)%mod;
            }
            while(x<n[i].x)
            {
                int a,b;
                exgcd(y-x+1,mod,a,b);
                if(a<0)a+=mod;
                ans=(ans*(LL)(sum[c[x]])%mod)%mod;
                ans=(ans*(LL)a)%mod;
                sum[c[x]]--;
                x++;
            }
            while(y>n[i].y)
            {
                int a,b;
                exgcd(y-x+1,mod,a,b);
                if(a<0)a+=mod;
                ans=(ans*(LL)sum[c[y]]%mod)%mod;
                ans=(ans*(LL)a)%mod;
                sum[c[y]]--;
                y--;
            }

            n[i].ans=ans;
        }
        sort(n,n+m,comp);
        for(int i=0; i<m; i++)
            printf("%d\n",n[i].ans);
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值