简单莫对算法 --- 离线处理所有询问

思想: 先把所有的询问全部记下来. 然后通过一定的技巧使得暴力的复杂度不高, 从而达到目的, 一次性全部输出答案.

以这两道模板题来说明一下它如何暴力的更漂亮.
复杂度为 O n ^ 3/2 ; (2e5是可以跑的)
小Z的袜子
AC Code

#include 
#define ll long long
using namespace std;
const int maxn = 5e4+5;
int a[maxn];
ll fz[maxn],fm[maxn];
int times[maxn];
int block;
struct node
{
    int l,r,id;
}s[maxn];

bool cmp(node x,node y)
{
    if(x.l/block==y.l/block)
        return x.r<y.r;
    return x.l<y.l;
}

int main()
{
    int n,q;
    scanf("%d%d",&n,&q);
    for(int i=1; i<=n; i++)
        scanf("%d",&a[i]);
    for(int i=1; i<=q; i++){
        scanf("%d%d",&s[i].l,&s[i].r);
        s[i].id=i;
    }
    block=(int)sqrt(n+0.5);
    sort(s+1,s+q+1,cmp);
    int l = 1 , r = 1;    //范围是[l,r);
    ll sum = 0;
    for(int i=1;i<=q;i++){
        while(l < s[i].l){
            times[a[l]]--;
            sum -= times[a[l]] ;   //唯一需要点推理的就是多加一次少加一次对sum总体的影响. 即times怎么变.
            l++;
        }
        while(l > s[i].l){
            l--;
            sum += times[a[l]];
            times[a[l]]++;
        }
        while(r < s[i].r+1){   //所以注意下范围然后就可以写出来了.
            sum += times[a[r]];
            times[a[r]]++;
            r++;
        }
        while(r > s[i].r+1){
            r--;
            times[a[r]]--;
            sum -= times[a[r]];
        }
        ll len = s[i].r+1-s[i].l;
        ll t = __gcd(len*(len-1)/2,sum);
        fz[s[i].id] = sum/t;
        fm[s[i].id] = len*(len-1)/2/t ;
    }
    for(int i=1;i<=q;i++){
        printf("%lld/%lld\n",fz[i],fm[i]);
    }
}

Powerful Array

#include 
#define ll long long
using namespace std;
const int maxn = 2e5+5;
int a[maxn];
ll res[maxn];
int times[maxn * 5];
int block;
struct node
{
    int l,r,id;
}s[maxn];

bool cmp(node x,node y)
{
    if(x.l/block==y.l/block)
        return x.r<y.r;
    return x.l<y.l;
}

int main()
{
    int n,q;
    scanf("%d%d",&n,&q);
    for(int i=1; i<=n; i++)
        scanf("%d",&a[i]);
    for(int i=1; i<=q; i++){
        scanf("%d%d",&s[i].l,&s[i].r);
        s[i].id=i;
    }
    block=(int)sqrt(n+0.5);
    sort(s+1,s+1+q,cmp);
    int l = 1 , r = 1;    //范围是[l,r);
    ll sum = 0;
    for(int i=1;i<=q;i++){
        while(l < s[i].l){
            times[a[l]]--;
            sum -= a[l] * (times[a[l]] * 2 + 1);
            l++;
        }
        while(l > s[i].l){
            l--;
            sum += a[l] * (times[a[l]] * 2 + 1);
            times[a[l]]++;
        }
        while(r < s[i].r+1){   //所以注意下范围然后就可以写出来了.
            sum += a[r] * (times[a[r]] * 2 + 1);
            times[a[r]]++;
            r++;
        }
        while(r > s[i].r+1){
            r--;
            times[a[r]]--;
            sum -= a[r] * (times[a[r]] * 2 + 1);
        }
        res[s[i].id] = sum;
    }
    for(int i=1;i<=q;i++)
        printf("%lld\n",res[i]);
}

你可能感兴趣的:(莫对算法)