HYSBZ 2038 小Z的袜子(hose) (莫队模板题)

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2038

解题思路:

莫队模板题。

对讯问排序,用到分块,

排序第一关键字为区间左端所在块

第二关键字为区间右的大小

至于为什么按照块排,数学上应该可以证明这么分复杂最小。

对于区间左右的延伸或者缩小。

只要不发生区间左比右大,右比左大就可以了。

所以我是先扩展,再收缩。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#include<cmath>
using namespace std;

const int N = 5e4+5;

int gcd(int a,int b){return a%b==0?b:gcd(b,a%b);}

int blo,bl[N],n,m;
struct node
{
    int l,r;
    int id;
    bool operator < (const node& a)const{
        return (bl[l]<bl[a.l])||(bl[l]==bl[a.l] && r<a.r);
    }
}q[N];

int l[N],r[N];
int cnt[N],ans[N],col[N];///当前颜色为i的袜子总数,答案,第i双袜子的颜色

int sum;///当前可以配对的总方法数

void Remove(int x)
{
    cnt[col[x]]--;
    sum -= cnt[col[x]];
}

void Add(int x)
{
    sum += cnt[col[x]];
    cnt[col[x]]++;
}

void solve()
{
    int nowl = 1,nowr = 0;
    for (int i=1;i<=m;i++){
        int tl = q[i].l,tr = q[i].r;
        while (nowr<tr) Add(++nowr);///先两边扩展好再收缩
        while (nowl>tl) Add(--nowl);
        while (nowr>tr) Remove(nowr--);
        while (nowl<tl) Remove(nowl++);
        ans[q[i].id] = sum;
    }
}

int main()
{
    scanf("%d %d",&n,&m);
    blo = sqrt(n);
    for (int i=1;i<=n;i++) scanf("%d",col+i),bl[i] = (i-1)/blo+1;
    for (int i=1;i<=m;i++){
        scanf("%d %d",&q[i].l,&q[i].r);
        l[i] = q[i].l;
        r[i] = q[i].r;
        q[i].id = i;
    }
    sort(q+1,q+1+m);
    solve();

    for (int i=1;i<=m;i++){
        ll fenzi = ans[i];
        //printf("ans[%d]=%d\n",i,ans[i]);
        if (ans[i]==0){
            printf("0/1\n");
        }
        else {
            ll fenmu = (ll)(r[i]-l[i]+1)*(r[i]-l[i])/2;///这里不用longlong会爆掉 50000*50000=25 0000 0000
            ll gg = gcd(fenzi,fenmu);
            fenzi /= gg;
            fenmu /= gg;
            printf("%lld/%lld\n",fenzi,fenmu);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43768644/article/details/94412195
今日推荐