zoukankan      html  css  js  c++  java
  • P1494 [国家集训队]小Z的袜子

    (color{#0066ff}{ 题目描述 })

    作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿。终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命……

    具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬。

    你的任务便是告诉小Z,他有多大的概率抽到两只颜色相同的袜子。当然,小Z希望这个概率尽量高,所以他可能会询问多个(L,R)以方便自己选择。

    然而数据中有L=R的情况,请特判这种情况,输出0/1。

    (color{#0066ff}{输入格式})

    输入文件第一行包含两个正整数N和M。N为袜子的数量,M为小Z所提的询问的数量。接下来一行包含N个正整数Ci,其中Ci表示第i只袜子的颜色,相同的颜色用相同的数字表示。再接下来M行,每行两个正整数L,R表示一个询问。

    (color{#0066ff}{输出格式})

    包含M行,对于每个询问在一行中输出分数A/B表示从该询问的区间[L,R]中随机抽出两只袜子颜色相同的概率。若该概率为0则输出0/1,否则输出的A/B必须为最简分数。(详见样例)

    (color{#0066ff}{输入样例})

    6 4
    1 2 3 3 3 2
    2 6
    1 3
    3 5
    1 6
    

    (color{#0066ff}{输出样例})

    2/5
    0/1
    1/1
    4/15
    

    (color{#0066ff}{数据范围与提示})

    30%的数据中 N,M ≤ 5000;

    60%的数据中 N,M ≤ 25000;

    100%的数据中 N,M ≤ 50000,1 ≤ L < R ≤ N,Ci ≤ N。

    (color{#0066ff}{ 题解 })

    莫队第一题。。。

    莫队的使用有一些限制

    首先,问题必须离线

    然后,要支持分块

    其次,对于区间的转移要很快,一般为(O(1))

    这题,显然一个区间的答案为(frac{sum C_{t_i}^2}{C_{len}^2})

    其中len为区间长度,(t_i)为颜色为i的袜子在区间中的个数

    化简之后变为了(frac{sum t_i*(t_i-1)}{len *(len-1)})

    发现分母是固定的,这题又要单独输出分子分母,于是只看分子就行了

    显然来了一个数或删掉一个数,我们都可以把当前贡献减掉再加上新的贡献

    于是第二个条件满足了, 第一个显然满足,第二个,由于是区间查询,也满足了

    那么怎么排序才能保证复杂度呢

    我们对于左端点在一个块的,按块的奇偶对r排序,那么r是一个波浪形

    如果左端点不在一个块,则按块的编号排序

    那么实际上l的移动都是在一个块内,移动完再去下一个块

    r的移动是一个波浪,在一个块内单调

    那么总复杂度显然是(O(nsqrt n))

    #include<bits/stdc++.h>
    #define LL long long
    LL in() {
    	char ch; LL x = 0, f = 1;
    	while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    	for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    	return x * f;
    }
    using std::pair;
    using std::make_pair;
    const int maxn = 5e4 + 10;
    int bel[maxn], n, m, a[maxn];
    LL t[maxn];
    struct node {
    	LL l, r, id;
    	friend bool operator < (const node &a, const node &b) {
    		if(bel[a.l] == bel[b.l]) return bel[a.l] & 1? a.r < b.r : a.r > b.r;
    		return bel[a.l] < bel[b.l];
    	}
    	LL tot() { return (r - l + 1) * (r - l); }
    }e[maxn];
    pair<LL, LL> ans[maxn];
    LL tot;
    LL l = 1, r;
    void add(int i) {
    	t[i]++;
    	if(t[i] >= 2) tot = tot + t[i] * (t[i] - 1) - (t[i] - 1) * (t[i] - 2);
    }
    void del(int i) {
    	t[i]--;
    	if(t[i] >= 1) tot = tot + t[i] * (t[i] - 1) - t[i] * (t[i] + 1);
    }
    LL gcd(LL x, LL y) { return y? gcd(y, x % y) : x; }
    void out(const pair<LL, LL> &a) {
    	LL x = a.first, y = a.second;
    	LL gc = gcd(x, y);
    	x /= gc, y /= gc;
    	printf("%lld/%lld
    ", x, y);
    }
    	
    int main() {
    	n = in(), m = in();
    	int len = sqrt(n);
    	for(int i = 1; i <= n; i++) a[i] = in(), bel[i] = (i - 1) / len + 1;
    	for(int i = 1; i <= m; i++) e[i].l = in(), e[i].r = in(), e[i].id = i;
    	std::sort(e + 1, e + m + 1);
    	for(int i = 1; i <= m; i++) {
    		while(l > e[i].l) add(a[--l]);
    		while(l < e[i].l) del(a[l++]);
    		while(r < e[i].r) add(a[++r]);
    		while(r > e[i].r) del(a[r--]);
    		if(e[i].l == e[i].r) ans[e[i].id] = make_pair(0, 1);
    		else ans[e[i].id] = make_pair(tot, e[i].tot());
    	}
    	for(int i = 1; i <= m; i++) out(ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    最小圆覆盖
    BZOJ3572 [Hnoi2014]世界树 【虚树 + 树形dp】
    一些组合数学
    BZOJ3611 [Heoi2014]大工程 【虚树】
    线段树合并
    BZOJ4446 [Scoi2015]小凸玩密室 【树形Dp】
    生成函数小记
    BZOJ2337 [HNOI2011]XOR和路径 【概率dp + 高斯消元】
    连续数字异或和
    POJ2976:Dropping tests——题解
  • 原文地址:https://www.cnblogs.com/olinr/p/10388965.html
Copyright © 2011-2022 走看看