题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2038
注:现在老版bzoj关闭了注册,大家需要修改自己的hosts文件,然后取新版bzoj注册,再去老版的做题
注册方式:https://www.lydsy.com/JudgeOnline/wttl/thread.php?tid=5671
在修改hosts文件时,需要权限,提高权限的操作如下:右键 —> 属性 —> 安全 —> 高级 —> 更改权限 —> 添加 —> 选择主体—> 高级 —> 立即查找 —> 找到当前所用用户 —> 确定 —> 将权限都选上 OK
在网上好不容易找到的莫队当年的解题报告:https://wenku.baidu.com/view/18ffb44476c66137ee061952.html
先 %%%% 一发莫队大神
小Z的袜子这题是一道询问统计型问题,它没有让你做出修改等操作,因此,如何高效的进行区间查询操作,就是莫队算法了,从朴素的暴力法来看,最大的问题在于,我们会遇到多个区间互相重合的情况,传统的线段树在最劣情况下仍会TLE,所以我们就要搞一些骚操作了,对于区间端点进行更新,并同时将序列Z划分位最上尽量少的不升(降)子序列。
关于如何slove这一部分操作,借用一下别人的讲解:
我们设想有一条数轴:
当curL < L 时,我们当前curL是已经处理好的了。所以remove时先去除当前curL再++
当curL > L 时,我们当前curL是已经处理好的了。所以 add 时先--再加上改后curL
当curR > R 时,我们当前curR是已经处理好的了。所以remove时先去除当前curR再--
当curR < R 时,我们当前curR是已经处理好的了。所以 add 时先++再加上改后curR
AC代码:
//看命题报告时还算看懂了他的意思,就是这代码....
#include <bits/stdc++.h>
using namespace std;
const int N = 50005;
typedef long long ll;
int n, m, pos[N], c[N];
ll s[N], ans;
ll gcd(ll a, ll b){return b == 0 ? a : gcd(b, a%b);}
ll sqr(ll x){return x*x;}
struct data{
int l, r, id;
ll a, b;
}a[N];
bool cmp(data a, data b){
if(pos[a.l] == pos[b.l]) return a.r < b.r;
return a.l < b.l;
}
bool cmp_id(data a, data b){return a.id < b.id;}
void init()
{
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; ++i)
scanf("%d", &c[i]);
int block = int(sqrt(n));
for(int i = 1; i <= n; ++i)
pos[i] = (i-1)/block+1; //将n个数分成sqrt(n)块
for(int i = 1; i <= m; ++i){
scanf("%d %d", &a[i].l, &a[i].r);
a[i].id = i;
}
}
void update(int p, int add) //对于区间端点的更新
{
ans -= sqr(s[c[p]]);
s[c[p]] += add;
ans += sqr(s[c[p]]);
}
void solve()
{
for(int i = 1, l = 1, r = 0; i <= m; ++i){
for(; r < a[i].r; r++) update(r+1, 1);
for(; r > a[i].r; r--) update(r, -1);
for(; l < a[i].l; l++) update(l, -1);
for(; l > a[i].l; l--) update(l-1, 1);
if(a[i].l == a[i].r){
a[i].a = 0;
a[i].b = 1;
continue;
}
a[i].a = ans - (a[i].r - a[i].l + 1);
a[i].b = (ll)(a[i].r - a[i].l + 1) * (a[i].r - a[i].l);
ll k = gcd(a[i].a, a[i].b);
a[i].a /= k;
a[i].b /= k;
}
}
int main()
{
init();
sort(a+1, a+m+1, cmp); //对询问区间的排序
solve();
sort(a+1, a+m+1, cmp_id);
for(int i = 1; i <= m; ++i){
cout << a[i].a << "/" << a[i].b << '\n';
}
return 0;
}