【BZOJ 2038】小Z的袜子

来源:互联网 发布:鲁迅 祝福 知乎 编辑:程序博客网 时间:2024/05/01 09:22

题目来源:BZOJ 2038

思路:

莫队算法。
先考虑计算答案的表达式,如果一种颜色xiyi个,那么,在一段[l,r]的区间中

ans=C2yiC2rl+1=yi!2!(yi2)!2!(rl+1)!(rl+1)!=yi(yi1)(rl+1)(rl)

分式下面的值为常数,所以说我们现在要求的就是分子的部分。

(下面转自hzwer的博客)

如果我们已知[l,r]的答案,能在O(1)时间得到[l+1,r]的答案以及[l,r1]的答案,即可使用莫队算法。时间复杂度为O(n1.5)。如果只能在logn的时间移动区间,则时间复杂度是O(n1.5logn)
其实就是找一个数据结构支持插入、删除时维护当前答案。
这道题的话我们很容易用数组来实现,做到O(1)的从[l,r]转移到[l,r+1][l+1,r]
转移具体的代码:

void update(int p, int k){    res -= s[c[p]] * (s[c[p]] - 1);    s[c[p]] += k;    res += s[c[p]] * (s[c[p]] - 1);}

那么莫队算法怎么做呢?
以下都是在转移为O(1)的基础下讨论的时间复杂度。另外由于n与m同阶,就统一写n。
如果已知[l,r]的答案,要求[l,r]的答案,我们很容易通过|ll|+|rr|次转移内求得。
将n个数分成n块。
按区间排序,以左端点所在块内为第一关键字,右端点为第二关键字,进行排序,也就是以(pos[l],r)排序。
然后按这个排序直接暴力,复杂度分析是这样的:
1. ii+1在同一块内,r单调递增,所以rO(n)的。由于有n0.5块,所以这一部分时间复杂度是n1.5
2. ii+1跨越一块,r最多变化n,由于有n0.5块,所以这一部分时间复杂度是n1.5
3. ii+1在同一块内时l变化不超过n0.5,跨越一块也不会超过n0.5,忽略2。由于有m次询问(和n同级),所以时间复杂度是n1.5
于是就是O(n1.5)

代码:

#include <cstdio>#include <algorithm>#include <iostream>#include <cmath>using namespace std;typedef long long ll;const int maxn = 50010;struct node{    int l, r, id; ll a, b;}v[maxn];int each, n, m, res;ll c[maxn], pos[maxn], s[maxn];bool cmp(node a, node b){return pos[a.l] == pos[b.l] ? a.r < b.r : a.l < b.l;}bool cmp_id(node a, node b){return a.id < b.id;}void update(int p, int k){    res -= s[c[p]] * (s[c[p]] - 1);    s[c[p]] += k;    res += s[c[p]] * (s[c[p]] - 1);}int main(){    scanf("%d%d", &n, &m), each = (int)sqrt(n);    for(int i = 1; i <= n; i ++) scanf("%d", &c[i]);    for(int i = 1; i <= n; i ++) pos[i] = (i-1)/each+1;    for(int i = 1; i <= m; i ++) scanf("%d%d", &v[i].l, &v[i].r), v[i].id = i;    sort(v+1, v+1+m, cmp);    for(int i = 1, l = 1, r = 0; i <= m; i ++){        for( ; r < v[i].r; r ++) update(r+1, 1);        for( ; r > v[i].r; r --) update(r, -1);        for( ; l < v[i].l; l ++) update(l, -1);        for( ; l > v[i].l; l --) update(l-1, 1);        if(v[i].l == v[i].r){v[i].a = 0, v[i].b = 1; continue;}        v[i].a = res, v[i].b = (ll)(r-l+1)*(r-l);        ll k = __gcd(v[i].a, v[i].b);        v[i].a /= k, v[i].b /= k;    } sort(v+1, v+1+m, cmp_id);    for(int i = 1; i <= m; i ++) printf("%lld/%lld\n", v[i].a, v[i].b);    return 0;}
0 0
原创粉丝点击