zoukankan      html  css  js  c++  java
  • 算法总结篇---莫队

    算法简介

    发明者:队爷莫涛

    一种“优雅的暴力”

    因善于无限卡常而闻名OI界

    主要思想也十分简单:

    暴力,也用到了排序,分块。

    实际的操作方式就是通过改变枚举区间的顺序来达到优化复杂度的目的

    虽然听上去很玄学,但却十分有效

    复杂度在 (O(n^{frac{3}{2}})) 左右

    一般是将查找区间分成 (n^{frac{1}{2}})

    更优的复杂度可以进行按块奇偶排序来实现

    普通莫队只支持离线操作,在线操作还是十分鸡肋的

    但难不倒我们OIer

    后人又总结出回滚莫队带修莫队树上莫队等多种方法,还没学,先咕咕着

    要注意枚举的顺序,详见(Oi-Wiki)关于四个循环问题的讨论,主要思路是将区间先扩大再缩小

    例题

    小B的询问

    小B的询问

    Solution

    利用莫队思想对所有查询的区间进行分块排序,顺便加上奇偶优化

    每增加一个数,它对答案的贡献等于 (c^{2} - (c - 1)^{2})

    每减掉一个数,它对答案的贡献等于 ((c - 1)^{2} - c^{2})

    Code

    /*
    Work by: Suzt_ilymics
    Knowledge: ??
    Time: O(??)
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define LL long long
    
    using namespace std;
    const int MAXN = 5e4+5;
    const int INF = 1;
    const int mod = 1;
    struct node{
    	int l, r, bh;
    }b[MAXN];
    
    int read(){
    	int s = 0, w = 1;
    	char ch = getchar();
    	while(ch < '0' || ch > '9') { if(ch == '-') w = -1; ch = getchar(); }
    	while(ch >= '0' && ch <= '9') s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
    	return s * w;
    }
    
    int n, m, k, qrtn;
    int a[MAXN];
    LL Ans[MAXN], ans;
    int cnt[MAXN];
    
    bool cmp(node x, node y){ return x.l / qrtn == y.l / qrtn ? (x.l / qrtn % 2 == 0 ? x.r > y.r : x.r < y.r) : x.l < y.l; }
    
    void add(LL pos){
    	int x = ++cnt[a[pos]];
    	ans -= (x - 1) * (x - 1);
    	ans += x * x;
    }
    
    void del(LL pos){
    	int x = --cnt[a[pos]];
    	ans -= (x + 1) * (x + 1);
    	ans += x * x;
    }
    
    int main()
    {
    	n = read(), m = read(), k = read();
    	for(int i = 1; i <= n; ++i) a[i] = read();
    	for(int i = 1; i <= m; ++i){
    		b[i].l = read(), b[i].r = read();
    		b[i].bh = i;
    	}
    	qrtn = sqrt(n + 0.5);
    	sort(b + 1, b + m + 1, cmp);
    	int l = 1, r = 0;
    	for(int i = 1; i <= m; ++i){
    		while(l > b[i].l) add(--l);
    		while(r < b[i].r) add(++r);
    		while(l < b[i].l) del(l++);
    		while(r > b[i].r) del(r--);
    		Ans[b[i].bh] = ans;
    	}
    	for(int i = 1; i <= m; ++i){
    		printf("%lld
    ", Ans[i]);
    	}
    	return 0;
    }
    

    [国家集训队]小Z的袜子

    [国家集训队]小Z的袜子

    Solution

    不难发现,所有情况总数等于 (len * (len - 1) / 2),其中 (len) 是区间长度

    设函数 (c(x) = x * (x - 1) / 2)

    则每加一个元素对其的贡献为 (c(x) - c(x - 1))

    同理减一个元素的贡献 (c(x - 1) - c(x))

    最后的答案等于分子和分母的比,分子为每个元素的贡献,分母为 (c(len)),注意通分和特判特殊情况

    Code

    /*
    Work by: Suzt_ilymics
    Knowledge: ??
    Time: O(??)
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define LL long long
    
    using namespace std;
    const int MAXN = 5e4+5;
    const int INF = 1;
    const int mod = 1;
    
    struct node{
    	int l, r, bh;
    }b[MAXN];
    
    inline int read(){
    	int s = 0, w = 1;
    	char ch = getchar();
    	while(ch < '0' || ch > '9') { if(ch == '-') w = -1; ch = getchar(); }
    	while(ch >= '0' && ch <= '9') s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
    	return s * w;
    }
    
    int n, m, qrtn;
    int a[MAXN];
    int cnt[MAXN];
    LL Ans[MAXN], ans;
    LL Ansfm[MAXN];
    
    inline bool cmp(node x, node y){ return x.l / qrtn == y.l / qrtn ? (x.l / qrtn % 2 == 0 ? x.r > y.r : x.r < y.r) : x.l < y.l; }
    inline LL Gcd(LL x, LL y){ return y == 0 ? x : Gcd(y, x % y); }
    
    inline LL c(LL x){	return x * (x - 1) / 2; }
    
    inline void del(LL pos){
    	int x = --cnt[a[pos]];
    	ans -= c(x + 1);
    	ans += c(x);
    }
    
    inline void add(LL pos){
    	int x = ++cnt[a[pos]];
    	ans -= c(x - 1);
    	ans += c(x);
    }
    
    int main()
    {
    	n = read(), m = read();
    	for(register int i = 1; i <= n; ++i) a[i] = read();
    	for(register int i = 1; i <= m; ++i){
    		b[i].l = read(), b[i].r = read();
    		b[i].bh = i;
    	}
    	qrtn = sqrt(n + 0.5);
    	sort(b + 1, b + m + 1, cmp);
    	int l = 0, r = -1;
    	for(register int i = 1; i <= m; ++i){
    		while(l > b[i].l){ l--; add(l); }
    		while(r < b[i].r){ r++; add(r);	}
    		while(l < b[i].l){ del(l); l++;	}
    		while(r > b[i].r){ del(r); r--;	}
    		Ans[b[i].bh] = ans;//fenzi
    		Ansfm[b[i].bh] = c(r - l + 1);//fenmu
    	}
    //	cout<<qrtn<<endl;
    	for(int i = 1; i <= m; ++i){
    		if(!Ans[i]) printf("0/1
    ");
    		else{
    			LL gcd = Gcd(Ans[i], Ansfm[i]);
    			printf("%lld/%lld
    ", Ans[i] / gcd, Ansfm[i] / gcd);	
    		}
    	}
    	return 0;
    }
    

    数列找不同

    数列找不同

    Solution

    (ans) 来记录有几个数相同

    如果增加一个元素 (x) 后,(cnt[x] == 2)(ans++)

    如果删掉一个元素 (x) 后,(cnt[x] == 1)(ans--)

    最后统计答案时,(ans == 1),为 (Yes),否则为 (No)

    Code

    /*
    Work by: Suzt_ilymics
    Knowledge: ??
    Time: O(??)
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define LL long long
    
    using namespace std;
    const int MAXN = 1e5+5;
    const int INF = 1;
    const int mod = 1;
    struct node{
    	int l, r, bh;
    }b[MAXN];
    
    int read(){
    	int s = 0, w = 1;
    	char ch = getchar();
    	while(ch < '0' || ch > '9') { if(ch == '-') w = -1; ch = getchar(); }
    	while(ch >= '0' && ch <= '9') s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
    	return s * w;
    }
    
    int n, m, k, qrtn;
    int a[MAXN];
    LL Ans[MAXN], ans;
    int cnt[MAXN];
    
    bool cmp(node x, node y){ return x.l / qrtn == y.l / qrtn ? (x.l / qrtn % 2 == 0 ? x.r > y.r : x.r < y.r) : x.l < y.l; }
    
    void add(LL pos){
    	int x = ++cnt[a[pos]];
    	if(x == 2) ans++;
    }
    
    void del(LL pos){
    	int x = --cnt[a[pos]];
    	if(x == 1) ans--;
    }
    
    int main()
    {
    	n = read(), m = read();
    	for(int i = 1; i <= n; ++i) a[i] = read();
    	for(int i = 1; i <= m; ++i){
    		b[i].l = read(), b[i].r = read();
    		b[i].bh = i;
    	}
    	qrtn = sqrt(n + 0.5);
    	sort(b + 1, b + m + 1, cmp);
    	int l = 1, r = 0;
    	for(int i = 1; i <= m; ++i){
    		while(l > b[i].l) add(--l);
    		while(r < b[i].r) add(++r);
    		while(l < b[i].l) del(l++);
    		while(r > b[i].r) del(r--);
    		Ans[b[i].bh] = (ans == 0) ? 1 : 0;
    	}
    	for(int i = 1; i <= m; ++i){ Ans[i] == 1 ? printf("Yes
    ") : printf("No
    "); }
    	return 0;
    }
    

    Little Elephant and Array

    Little Elephant and Array

    Solution

    每加一个数,统计个数,看看有没有新的贡献。

    注意每加一个数或减一个数都有可能做出贡献,如果多加了也要将相应的贡献减掉

    Code

    没加任何优化的莫队,当时第一次写,写的比较丑

    /*
    Work by: Suzt_ilymics
    Knowledge: ??
    Time: O(??)
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    
    using namespace std;
    const int MAXN = 1e5+5;
    const int INF = 1;
    const int mod = 1;
    
    struct node{
    	int l, r, bh, ans;
    	bool operator < (const node &b) const {return l == b.l ? r < b.r : l < b.l; }
    }b[MAXN];
    
    int read(){
    	int s = 0, w = 1;
    	char ch = getchar();
    	while(ch < '0' || ch > '9') { if(ch == '-') w = -1; ch = getchar(); }
    	while(ch >= '0' && ch <= '9') s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
    	return s * w;
    }
    
    int n, m, l = 1, r = 0, ans = 0;
    int a[MAXN], cnt[MAXN];
    bool vis[MAXN];
    
    bool cmp(node x, node y){return x.l == y.l ? x.r < y.r : x.l < y.l; }
    bool cmp2(node x, node y){return x.bh < y.bh; }
    
    int main()
    {
    	n = read(), m = read();
    	for(int i = 1; i <= n; ++i) a[i] = read(), a[i] = a[i] > 1e5 ? 0 : a[i];
    	for(int i = 1; i <= m; ++i) b[i].l = read(), b[i].r = read(), b[i].bh = i;
    	sort(b + 1, b + m + 1, cmp);
    	for(int i = 1; i <= m; ++i){
    		while(l < b[i].l) {
    			cnt[a[l]]--; l++;
    			if(a[l - 1] == cnt[a[l - 1]] && !vis[a[l - 1]]) ans++, vis[a[l - 1]] = 1;
    			if(a[l - 1] != cnt[a[l - 1]] && vis[a[l - 1]]) ans--, vis[a[l - 1]] = 0;
    		}
    		while(r < b[i].r) {
    			cnt[a[++r]]++;
    			if(a[r] == cnt[a[r]] && !vis[a[r]]) ans++, vis[a[r]] = 1;
    			if(a[r] != cnt[a[r]] && vis[a[r]]) ans--, vis[a[r]] = 0;
    		}
    		while(r > b[i].r){
    			cnt[a[r]]--; r--;
    			if(a[r + 1] == cnt[a[r + 1]] && !vis[a[r + 1]]) ans++, vis[a[r + 1]] = 1;
    			if(a[r + 1] != cnt[a[r + 1]] && vis[a[r + 1]]) ans--, vis[a[r + 1]] = 0;
    		}
    		b[i].ans = ans;
    	}
    	sort(b + 1, b + m + 1, cmp2);
    	for(int i = 1; i <= m; ++i){
    		printf("%d
    ", b[i].ans);
    	}
    	return 0;
    }
    

    XOR and Favorite Number

    CF617E XOR and Favorite Number

    Solution

    参考这位大佬的博客

    Code

    /*
    Work by: Suzt_ilymics
    Knowledge: ??
    Time: O(??)
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define LL long long
    #define int long long 
    
    using namespace std;
    const int MAXN = 1e5+5;
    const int kMax = 1e6+6;
    const int INF = 1;
    const int mod = 1;
    struct node{
    	int l, r, bh;
    }b[MAXN];
    
    int n, m, k, qrtn;
    int a[MAXN];
    LL Ans[MAXN], ans;
    int cnt[kMax << 1];
    
    int read(){
    	int s = 0, w = 1;
    	char ch = getchar();
    	while(ch < '0' || ch > '9') { if(ch == '-') w = -1; ch = getchar(); }
    	while(ch >= '0' && ch <= '9') s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
    	return s * w;
    }
    
    bool cmp(node x, node y) { return x.l / qrtn == y.l / qrtn ? x.r < y.r : x.l < y.l; }
    
    void add(int pos){
    	ans += (LL)cnt[a[pos] ^ k];
    	++cnt[a[pos]];
    }
    
    void del(int pos){
    	--cnt[a[pos]];
    	ans -= (LL)cnt[a[pos] ^ k];
    }
    
    signed main()
    {
    	n = read(), m = read(), k = read();
    	for(int i = 1; i <= n; ++i) a[i] = read(), a[i] ^= a[i - 1];
    	for(int i = 1; i <= m; ++i){
    		b[i].l = read(), b[i].r = read(), b[i].bh = i;
    	}
    	qrtn = sqrt(n + 0.5);
    	sort(b + 1, b + m + 1, cmp);
    	int l = 0, r = -1;
    	cnt[0] = 1;
    	for(int i = 1; i <= m; ++i){
    		while(l > b[i].l) l--, add(l - 1);
    		while(r < b[i].r) r++, add(r);
    		while(l < b[i].l) del(l - 1), l++;
    		while(r > b[i].r) del(r), r--;
    		Ans[b[i].bh] = ans;
    	}
    	for(int i = 1; i <= m; ++i){
    		printf("%lld
    ", Ans[i]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    Leetcode Plus One
    Leetcode Swap Nodes in Pairs
    Leetcode Remove Nth Node From End of List
    leetcode Remove Duplicates from Sorted Array
    leetcode Remove Element
    leetcode Container With Most Water
    leetcode String to Integer (atoi)
    leetcode Palindrome Number
    leetcode Roman to Integer
    leetcode ZigZag Conversion
  • 原文地址:https://www.cnblogs.com/Silymtics/p/14130615.html
Copyright © 2011-2022 走看看