Hdu 5145 NPY and girls

 一个很普通的莫队题+可重集排列

莫队的说明网上已经很多了,我就不献丑了


直接来考虑添加端点或者删除端点的情况吧

添加 就是分子乘区间长度,分母乘当前位置的数的个数,然后更新数的个数

删除 就是分母乘区间长度,分子乘当前位置的数的个数,同样更新一下数的个数


莫队的我没有注意到的一个trick是

转移区间的时候优先更新右端点,然后左端点,否则可能出现右端点在左端点左边的情况


#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;

#define LL long long

const int maxn = 31234;
const LL mod = 1000000007;

struct Ask{
    int l,r;
    int id;
    LL ans;
    void init(int i){
        scanf("%d %d",&l,&r);
        id = i;
    }
};

Ask ask[maxn];
int unit;

bool cmpmo(Ask a,Ask b){
    if(a.l / unit != b.l / unit)
        return a.l < b.l;
    return a.r < b.r;
}

bool cmpid(Ask a,Ask b){
    return a.id < b.id;
}

LL cnt[maxn];
int arr[maxn];

LL rever[maxn];

LL powerMod(LL x,LL n,LL mod){
    LL ret = 1;
    while(n){
        if(n&1) (ret *= x) %= mod;
        (x *= x) %= mod;
        n >>=1;
    }
    return ret;
}

LL rev(int x){
    if(rever[x] != -1)
        return rever[x];
    return rever[x] = powerMod(x,mod-2,mod);
}

LL siz;

LL renew(LL ans,int pos,bool adder){
    LL & x = cnt[arr[pos]];
    if(adder){
        x++;
        siz++;
        (ans *= siz) %= mod;
        (ans *= rev(x)) %= mod;
    }
    else{
        (ans *= x) %= mod;
        (ans *= rev(siz)) %= mod;
        siz--;
        x--;
    }
    return ans;
}

int main(){
    int T;
    int n,m;
    memset(rever,-1,sizeof(rever));
    scanf("%d",&T);
    while(T-- && ~scanf("%d %d",&n,&m)){
        unit = (int)sqrt(n*1.0);
        for(int i=1;i<=n;i++){
            scanf("%d",&arr[i]);
        }
        for(int i=0;i<m;i++){
            ask[i].init(i);
        }
        sort(ask,ask+m,cmpmo);
        LL ans = 1;
        siz = 0;
        memset(cnt,0,sizeof(cnt));
        for(int i=ask[0].l;i<=ask[0].r;i++){
            ans = renew(ans,i,true);
        }
        ask[0].ans = ans;
        int st,ed;
        for(int i=1;i<m;i++){
            st = min(ask[i].r,ask[i-1].r);
            ed = max(ask[i].r,ask[i-1].r);
            for(int id=st+1;id<=ed;id++)
                ans = renew(ans,id,ed==ask[i].r);
            st = min(ask[i].l,ask[i-1].l);
            ed = max(ask[i].l,ask[i-1].l);
            for(int id=st;id<ed;id++)
                ans = renew(ans,id,st==ask[i].l);
            ask[i].ans = ans;
        }
        sort(ask,ask+m,cmpid);
        for(int i=0;i<m;i++){
            printf("%I64d\n",ask[i].ans);
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值