1. 程式人生 > 實用技巧 >[模板]莫隊/P3901 數列找不同

[模板]莫隊/P3901 數列找不同

目錄

[模板]莫隊/P3901 數列找不同

題目

傳送門

沒有專門的模板,就把這道題作為模板啦

演算法介紹

大致思想

聽說這題有O(n)預處理,O(1)詢問的方法,但是這不重要,還是先練練莫隊吧

《演算法競賽 進階指南》裡面沒有專門講莫隊,學得就有一丟丟麻煩

簡單說,莫隊就是

優雅的暴力

首先要注意:
莫隊是離線演算法

我們把詢問分成t塊(不一定十分精確,可能多出或缺少幾個塊,只是借用了分塊的思想),任意一個塊內,每一個詢問的左端點和其它詢問的左端點的差的絕對值不超過\(\sqrt n\),且塊內右端點升序排列.所以就有了進行stl sort的cmp

函式:

bool cmp(node a , node b) {
	return (a.l / t != b.l / t) ? (a.l / t < b.l / t) : (a.r < b.r);
}
//在主函式中:t=sqrt(n)

t到這裡就被已經拋棄了(毫無存在感)

對於排序後的每一個詢問,我們抹去上一個詢問的多出來的答案,並補充上一個詢問沒有的部分,得到當前詢問的答案,為了方便,一般寫成兩個函式:

void add(int l , int r) {
	if(l == 0)++l;
	if(r < l)return;
	for(int i = l ; i <= r ; i++) {
        
	}
}
void earse(int l , int r) {
	if(l == 0)++l;
	if(r < l)return;
	for(int i = l ; i <= r ; i++) {
        
	}
}

莫隊到這裡就介紹完了,接下來看看時間複雜度:

時間複雜度

很顯然,時間主要是花在了對詢問的處理上,所以我們只討論詢問處理部分
分兩種情況:

  1. 分塊的第一個詢問:由於塊和塊之間沒有比較大的約定,addearse兩個操作都有可能要用到\(O(n)\)的時間,又因為有\(\sqrt n\)個塊,所以時間複雜度為\(O(n\sqrt n)\)
  2. 其它情況:由於每一個塊內右端點是遞增的,所以一個塊對右端點的處理總共用時\(O(n)\),又因為一共有\(\sqrt n\)個塊,所以處理右端點總共用時\(O(n\sqrt n)\).對於左端點,由於分塊內的左端點之差不超過\(\sqrt n\),所以處理所有詢問
    的左端點共用時\(O(n\sqrt n)\)(這裡不單獨討論每一個塊是因為塊的長度不定,不如全部一起討論更容易理解)

綜上,時間複雜度為\(O(n\sqrt n)\)

程式碼

AC程式碼

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#define nn 100010
using namespace std;
int read() {
	int re = 0;
	char c = getchar();
	while(c < '0' || c > '9') c = getchar();
	while(c >= '0' && c <= '9')  
		re = (re << 1) + (re << 3) + c - '0',
		c = getchar();
	return re;
}
struct node{
	int l , r , id;
}ask[nn];
int t;
bool cmp(node a , node b) {
	return (a.l / t != b.l / t) ? (a.l / t < b.l / t) : (a.r < b.r);
}

int vis[nn];
int n , q;
int a[nn];
int check;
bool ans[nn];

void add(int l , int r) {
	if(l == 0)++l;
	if(r < l)return;
	for(int i = l ; i <= r ; i++) {
		++vis[a[i]];
		if(vis[a[i]] == 2)
			++check;
	}
}
void earse(int l , int r) {
	if(l == 0)++l;
	if(r < l)return;
	for(int i = l ; i <= r ; i++) {
		--vis[a[i]];
		if(vis[a[i]] == 1)
			--check;
	}
}
int main() {
	n = read() , q = read();
	for(int i = 1 ; i <= n ; i++)
		a[i] = read();
	
	for(int i = 1 ; i <= q ; i++) {
		ask[i].l = read() , ask[i].r = read() , ask[i].id = i;
	}
	t = sqrt(n);
	sort(ask + 1 , ask + q + 1 , cmp);
	
//	cout << endl;
//	for(int i = 1 ; i <= q ; i++)
//		cout << ask[i].l << ' ' << ask[i].r << endl;
//	return 0;
	for(int i = 1 ; i <= q ; i++) {
		earse(ask[i - 1].l , ask[i].l - 1);
		add(ask[i - 1].r + 1 , ask[i].r);
		
		earse(ask[i].r + 1 , ask[i - 1].r);
		add(ask[i].l , ask[i - 1].l - 1);
		ans[ask[i].id] = (check == 0);
		
	}
	for(int i = 1 ; i <= q ; i++)
		puts(ans[i] ? "Yes" : "No");
	return 0;
} 

資料生成

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#define nn 100010
using namespace std;
int read() {
	int re = 0;
	char c = getchar();
	while(c < '0' || c > '9') c = getchar();
	while(c >= '0' && c <= '9')  
		re = (re << 1) + (re << 3) + c - '0',
		c = getchar();
	return re;
}
struct node{
	int l , r , id;
}ask[nn];
int t;
bool cmp(node a , node b) {
	return (a.l / t != b.l / t) ? (a.l / t < b.l / t) : (a.r < b.r);
}

int vis[nn];
int n , q;
int a[nn];
int check;
bool ans[nn];

void add(int l , int r) {
	if(l == 0)++l;
	if(r < l)return;
	for(int i = l ; i <= r ; i++) {
		++vis[a[i]];
		if(vis[a[i]] == 2)
			++check;
	}
}
void earse(int l , int r) {
	if(l == 0)++l;
	if(r < l)return;
	for(int i = l ; i <= r ; i++) {
		--vis[a[i]];
		if(vis[a[i]] == 1)
			--check;
	}
}
int main() {
	n = read() , q = read();
	for(int i = 1 ; i <= n ; i++)
		a[i] = read();
	
	for(int i = 1 ; i <= q ; i++) {
		ask[i].l = read() , ask[i].r = read() , ask[i].id = i;
	}
	t = sqrt(n);
	sort(ask + 1 , ask + q + 1 , cmp);
	
//	cout << endl;
//	for(int i = 1 ; i <= q ; i++)
//		cout << ask[i].l << ' ' << ask[i].r << endl;
//	return 0;
	for(int i = 1 ; i <= q ; i++) {
		earse(ask[i - 1].l , ask[i].l - 1);
		add(ask[i - 1].r + 1 , ask[i].r);
		
		earse(ask[i].r + 1 , ask[i - 1].r);
		add(ask[i].l , ask[i - 1].l - 1);
		ans[ask[i].id] = (check == 0);
		
	}
	for(int i = 1 ; i <= q ; i++)
		puts(ans[i] ? "Yes" : "No");
	return 0;
} 

對拍

#include <bits/stdc++.h>
using namespace std;
int main() {
	int cnt = 0;
	while(true) {
		++cnt;
		putchar('\n');
		printf("#%d\n" , cnt);
		
		system("random.exe > input.txt");
		puts("random");
		
		int t = clock();
		if(system("tested2.exe < input.txt > output1.txt") != 0) {
			cout << "RE";
			break;
		}
		puts("tested2");
		t = clock() - t;
		printf(">time: %d\n" , t);
		if(t >= 1500) {
			cout << "TLE";
			break;
		}
		
		continue;
		
		system("std.exe < input.txt > output2.txt");
		puts("std");
		
		if(system("fc output1.txt output2.txt")) {
			cout << "WA";
			break;
		}
	}
	system("start input.txt");
	return 0;
}

暴力

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define nn 100010
using namespace std;
int read() {
	int re = 0;
	char c = getchar();
	while(c < '0' || c > '9') c = getchar();
	while(c >= '0' && c <= '9')  
		re = (re << 1) + (re << 3) + c - '0',
		c = getchar();
	return re;
}
int vis[nn];
int a[nn];
int n , q;
int main() {
	n = read(), q = read();
	for(int i = 1 ; i <= n ; i++)
		a[i] = read();
	
	for(int i = 1 ; i <= q ; i++) {
		memset(vis , 0 , sizeof(vis));
		int l = read() , r = read();
		for(int j = l ; j <= r ; j++)
			vis[a[j]]++;
		
		bool check = true;
		for(int j = l ; j <= r && check ; j++)
			if(vis[a[j]] > 1)
				check = false;
		puts(check ? "Yes" : "No");
	}
	return 0;
} 

50分(TLE)莫隊(我也不知道哪裡有問題)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define nn 100010
using namespace std;
int read() {
	int re = 0;
	char c = getchar();
	while(c < '0' || c > '9') c = getchar();
	while(c >= '0' && c <= '9')  
		re = (re << 1) + (re << 3) + c - '0',
		c = getchar();
	return re;
}
int vis[nn];
int a[nn];
int n , q;

struct QueryNode {
	int l , r;
	int id;
}ask[nn];
bool cmp0(QueryNode a , QueryNode b){return a.l < b.l;}
bool cmp1(QueryNode a , QueryNode b){return a.r < b.r;}
int ans[nn];

int t;
int L[110] , R[110];
int main() {
	n = read(), q = read();
	for(int i = 1 ; i <= n ; i++)
		a[i] = read();
	for(int i = 1 ; i <= q ; i++)
		ask[i].l = read() , ask[i].r = read() , ask[i].id = i;
	
	t = sqrt(q);
	R[0] = 0;
	for(int i = 1 ; i <= t ; i++)
		L[i] = R[i - 1] + 1 , R[i] = i * (q / t);
	if(R[t] < q)	t++ , L[t] = R[t - 1] + 1 , R[t] = q;
	
	sort(ask + 1 , ask + q + 1 , cmp0);
	for(int i = 1 ; i <= t ; i++)
		sort(ask + L[i] , ask + R[i] + 1 , cmp1);
	
//	for(int i = 1 ; i <= q ; i++)
//		cout << ask[i].l << ' ' << ask[i].r << endl;
	
	for(int i = 1 ; i <= t ; i++) {
		memset(vis , 0 , sizeof(vis));
		int check = 0;
		for(int j = ask[L[i]].l ; j <= ask[L[i]].r ; j++) {
			++vis[a[j]];
			if(vis[a[j]] == 2)
				++check;
		}
		ans[ask[L[i]].id] = (check == 0 ? true : false);
		
		
		for(int j = L[i] + 1 ; j <= R[i] ; j++) {
			if(ask[j - 1].l < ask[j].l)
				for(int k = ask[j - 1].l ; k < ask[j].l ; k++) {
					--vis[a[k]];
					if(vis[a[k]] == 1)
						check--;
				}
			else 
				for(int k = ask[j].l ; k < ask[j - 1].l ; k++) {
					++vis[a[k]];
					if(vis[a[k]] == 2)
						check++;
				}
				
			
			for(int k = ask[j - 1].r + 1 ; k <= ask[j].r ; k++) {
				++vis[a[k]];
				if(vis[a[k]] == 2)
					check++;
			}
			/*
			check = 0;
			for(int k = ask[j].l ; k <= ask[j].r ; k++)
				if(vis[a[k]] > 1)
					check++;*/
			ans[ask[j].id] = (check == 0 ? true : false);
		}
	}
	for(int i = 1 ; i <= q ; i++)
		puts(ans[i] ? "Yes" : "No");
	return 0;
}