首页 > 其他分享 >莫队

莫队

时间:2023-02-02 16:48:40浏览次数:45  
标签:cnt int pos -- while ans 莫队

简介

莫队是一种优美的暴力算法~(——by Daniel_yuan dalao)。

例题

例题出自:

莫队

莫队大全

[数据结构]莫队

(建议按顺序刷题~)

P3901 数列找不同

分析

板子,速速切掉!!!

代码

点击查看代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>

using namespace std;

inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}

const int N = 1e5 + 10;
int n, q, ans;
int a[N];
int block, b[N];

struct node{
	int l, r, id;
	bool operator < (const node &x) const{
		return (b[l] == b[x.l] ? r < x.r : l < x.l);
	}
}que[N];

int cnt[N];

void add(int pos){
	if (++cnt[a[pos]] == 1) ans++;
}

void del(int pos){
	if (--cnt[a[pos]] == 0) ans--;
}

int L, R;
bool sum[N];

int main(){
	n = read(), q = read();
	block = sqrt(n);
	for (int i = 1; i <= n; i++){
		a[i] = read();
		b[i] = (i - 1) / block + 1;
	}
	for (int i = 1; i <= q; i++){
		que[i].l = read(), que[i].r = read(), que[i].id = i;
	}
	sort(que + 1, que + q + 1);
	for (int i = 1; i <= q; i++){
		int l = que[i].l, r = que[i].r;
		while (L < l) del(L++);
		while (L > l) add(--L);
		while (R > r) del(R--);
		while (R < r) add(++R);
		if (ans == r - l + 1) sum[que[i].id] = 1;
	}
	for (int i = 1; i <= q; i++){
		printf("%s\n", (sum[i] ? "Yes" : "No"));
	}
	return 0;
} 

FREQUENT - Frequent values

分析

恶心的多测题,板子,但是莫队先扩张再收缩。

代码

点击查看代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
 
using namespace std;
 
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}
 
const int N = 1e5 + 10;
int n, m, k, ans;
int a[N], s[N];
int block, b[N];
 
struct node{
	int l, r, id;
	bool operator < (const node &x) const{
		return (b[l] == b[x.l] ? r < x.r : l < x.l);
	}
}q[N];
 
int cnt[N], tot[N];
 
void add(int pos){
	tot[cnt[a[pos]]]--;
	cnt[a[pos]]++;
	tot[cnt[a[pos]]]++;
	ans = max(ans, cnt[a[pos]]);
}
 
void del(int pos){
	tot[cnt[a[pos]]]--;
	if (tot[cnt[a[pos]]] == 0 && cnt[a[pos]] == ans) ans--;
	cnt[a[pos]]--;
	tot[cnt[a[pos]]]++;
}
 
int L = 1, R;
int sum[N];
 
int main(){
	while (n = read()){
		if (n == 0) break;
		memset(a, 0, sizeof a);
		memset(b, 0, sizeof b);
		memset(q, 0, sizeof q);
		memset(s, 0, sizeof s);
		memset(cnt, 0, sizeof cnt);
		memset(tot, 0, sizeof tot);
		memset(sum, 0, sizeof sum);
		m = read();
		block = sqrt(n);
		for (int i = 1; i <= n; i++){
			s[i] = a[i] = read();
			b[i] = (i - 1) / block + 1;
		}
		sort(s + 1, s + n + 1);
		int len = unique(s + 1, s + n + 1) - s - 1;
		for (int i = 1; i <= n; i++){
			a[i] = lower_bound(s + 1, s + len + 1, a[i]) - s;
		}
		for (int i = 1; i <= m; i++){
			q[i].l = read(), q[i].r = read(), q[i].id = i;
		}
		sort(q + 1, q + m + 1);
		L = 1, R = 0, ans = 0;
		for (int i = 1; i <= m; i++){
			int l = q[i].l, r = q[i].r;
			while (L > l) add(--L);
			while (R < r) add(++R);
			while (L < l) del(L++);
			while (R > r) del(R--);
			sum[q[i].id] = ans;
		}
		for (int i = 1; i <= m; i++){
			printf("%d\n", sum[i]);
		}
	}
	return 0;
}  

P1972 [SDOI2009] HH的项链

分析

前言:莫队不可过。
莫队板子,拿到 \(40+\) 分就ok了。

代码

本人写的是正解树状数组做法。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n,q,g[1145141],a[1145141],k[1145141],l,r,cq,as[1145141];
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}
struct ask{
	int l,r,num;
}qu[1145141];
bool cmp(ask a,ask b){
	return k[a.l]==k[b.l]?(k[a.l]%2==1?a.r<b.r:a.r>b.r):a.l<b.l;
}
void add(int k){
	g[a[k]]++;
	if(g[a[k]]==1) cq++;
}
void del(int k){
	g[a[k]]--;
	if(g[a[k]]==0) cq--;
}

int main(){
	n=read();
	for (register int i=1;i<=n;i++) a[i]=read();
	q=read();
	int sq=n*2/sqrt(n);
	for(register int i=1;i<=n;i++) k[i]=(i-1)/sq;
	for(register int i=1;i<=q;i++)
		qu[i].l=read(),qu[i].r=read(),qu[i].num=i;
	sort(qu+1,qu+q+1,cmp);
	for(register int i=1;i<=q;i++){
		while(l>qu[i].l){
			g[a[--l]]++;
			cq+=(g[a[l]]==1);
		}
		while(l<qu[i].l){
			g[a[l]]--;
			cq-=(g[a[l]]==0);		
			++l;
		}
		while(r<qu[i].r){
			g[a[++r]]++;
			cq+=(g[a[r]]==1);		
		}
		while(r>qu[i].r){
			g[a[r]]--;
			cq-=(g[a[r]]==0);
			--r;
		}
		
		as[qu[i].num]=cq;
	}
	for(register int i=1;i<=q;i++){
		write(as[i]);
		printf("\n");
	}
} 

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

分析

第一眼看上去,哇塞,莫队题,直接切掉。但是看到概率,直接就退缩了。其实只需要用可能次数除以总次数就ok了。
假如有 \(n\) 条相同颜色的袜子,则有 \(n + (n - 1) + (n - 2) + (n - 3) + \dots + 1\),然后我们可以发现这样就可以跑莫对了,总方案数就是

$$\tbinom{r - l + 1}{2}$$

然后约个分就写完了。(是不是很简单)

代码

点击查看代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define int long long

using namespace std;

inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}

const int N = 5e4 + 10;
int n, m, ans;
int a[N];
int block, b[N];

struct node{
	int l, r, id;
	bool operator < (const node &x) const{
		return (b[l] == b[x.l] ? r < x.r : l < x.l);
	}
}q[N];

int cnt[N];

void add(int pos){
	ans += cnt[a[pos]];
	cnt[a[pos]]++; 
}

void del(int pos){
	cnt[a[pos]]--;
	ans -= cnt[a[pos]];
}

int gcd(int a, int b){
	return !b ? a : gcd(b, a % b);
}

void get_gcd(int &a, int &b){
	int g = gcd(a, b);
	a /= g, b /= g;
}

int L = 1, R;
int sum1[N], sum2[N];

signed main(){
	n = read(), m = read();
	block = sqrt(n);
	for (int i = 1; i <= n; i++){
		a[i] = read();
		b[i] = (i - 1) / block + 1;
	}
	for (int i = 1; i <= m; i++){
		q[i].l = read(), q[i].r = read(), q[i].id = i;
	}
	sort(q + 1, q + m + 1);
	for (int i = 1; i <= m; i++){
		int l = q[i].l, r = q[i].r;
		if (l == r){
			sum1[q[i].id] = 0, sum2[q[i].id] = 1;
			continue;
		}
		while (L < l) del(L++);
		while (L > l) add(--L);
		while (R > r) del(R--);
		while (R < r) add(++R);
		sum1[q[i].id] = ans;
		sum2[q[i].id] = (r - l + 1) * (r - l) / 2;
		get_gcd(sum1[q[i].id], sum2[q[i].id]);
	}
	for (int i = 1; i <= m; i++){
		printf("%d/%d\n", sum1[i], sum2[i]);
	}
	return 0;
} 

P2709 小B的询问

分析

第一眼想到莫队,然后觉得需要前缀和优化。但真的有必要开个数组进行前缀和优化吗,其实直接用桶记录每个数出现次数,再按照题目的意思相加就行了。

代码

点击查看代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>

using namespace std;

inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}

const int N = 1e5 + 10;
int n, m, k, ans;
int a[N];
int block, b[N];

struct node{
	int l, r, id;
	bool operator < (const node &x) const{
		return (b[l] == b[x.l] ? r < x.r : l < x.l);
	}
}q[N];

int cnt[N];

void add(int pos){
	ans -= cnt[a[pos]] * cnt[a[pos]];
	cnt[a[pos]]++;
	ans += cnt[a[pos]] * cnt[a[pos]];
}

void del(int pos){
	ans -= cnt[a[pos]] * cnt[a[pos]];
	cnt[a[pos]]--;
	ans += cnt[a[pos]] * cnt[a[pos]];
}

int L = 1, R;
int sum[N];

int main(){
	n = read(), m = read(), k = read();
	block = sqrt(n);
	for (int i = 1; i <= n; i++){
		a[i] = read();
		b[i] = (i - 1) / block + 1;
	}
	for (int i = 1; i <= m; i++){
		q[i].l = read(), q[i].r = read(), q[i].id = i;
	}
	sort(q + 1, q + m + 1);
	for (int i = 1; i <= m; i++){
		int l = q[i].l, r = q[i].r;
		while (L < l) del(L++);
		while (L > l) add(--L);
		while (R > r) del(R--);
		while (R < r) add(++R);
		sum[q[i].id] = ans;
	}
	for (int i = 1; i <= m; i++){
		printf("%d\n", sum[i]);
	}
	return 0;
} 

标签:cnt,int,pos,--,while,ans,莫队
From: https://www.cnblogs.com/bryceyyds/p/17086441.html

相关文章

  • 简单分块与莫队
    1-分块1.1-定义分块是将要维护的信息分成若干块,而后通过维护整块的信息或者是块间的信息来优化算法。1.2-序列分块在序列上以线段树来类比,线段树是将序列每次对......
  • 莫队算法学习(转载)
    1.https://blog.csdn.net/Just__Do__IT__/article/details/118991059?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167326430316782428668181%2522%252C%252......
  • 莫队trick
    不带修(belong[a.l]^belong[b.l])?(belong[a.l]<belong[b.l]):((belong[a.l]&1)?a.r<b.r:a.r>b.r)待修(belong[a.l]^belong[b.l])?belong[a.l]<......
  • 莫队算法
    概念莫队算法是由莫涛提出的算法,可以解决一类离线区间询问问题,适用性极为广泛。同时将其加以扩展,便能轻松处理树上路径询问以及支持修改操作。假设\(n=m\),对于序列上的......
  • 带修改的莫队算法学习小记
    简介莫涛大神创造出的离线询问算法的带修改版。算法基础:需要掌握​​莫队算法​​,会打暴搜(暴力)。一个叫莫的双端队列。只支持单点修改操作方法普通的不带修改的莫队算......
  • 莫队
    暑假学的东西,现在才来写先推荐莫队算法——从入门到紫题&&dX的莫队题单普通莫队口胡一下时间复杂度(默认\(m,n\)同阶)左指针单次操作在块内移动次数为\(O(\sqrt{n})\),n次......
  • 普通莫队学习笔记
    莫队算法主要用于可以离线的区间询问回答。引子考虑一个这样的问题:假设没有事先求前缀和,你知道了数组第\(5\)个数到第\(100\)个数的和,现在询问问你第\(4\)个数到......
  • 莫队
    莫对是一种将区间询问离线处理的优雅的暴力。(主要思想:分块)普通莫队对于形如[l,r]的询问,莫队首先将所有询问存储下来,通过排序优化区间的转移,那么对于序列上的区间询......
  • 莫队
    莫队算法最初是由清华集训队莫涛队长在\(2009\)年整理后详细提出,是一种离线算法,主要是利用双指针,再基于分块思想解决一些区间查询问题,又被称为“优雅的暴力算法”。时间复......
  • 【XSY3490】线段树(广义线段树,树上莫队)
    题面线段树题解本题分两Part走。Part1我们需要解决:如何在广义线段树上快速区间定位节点。对于有\(n\)个叶子节点、共\(2n-1\)个节点的广义线段树\(A\),我们......