HDU 5145 NPY and girls 莫队算法

对于这类区间查询的问题,如果可以用O(1)的复杂度推到一个曼哈顿距离为1的另外区间的话,就可以直接用莫队算法搞。

从网上搜到的有两种搞法。第一种是先建立曼哈顿距离最小生成树,然后直接dfs遍历整棵树来求解的。

还有一种是先分块,然后把查询按照块的编号为第一关键字,右边界为第二关键字排序,排序直接直接暴力转移。

这样做的复杂度是n * sqrt(n),后面那个sqrt(n)取决于是怎么分块的。

仔细想想感觉这样子搞复杂度差不多就是这样,因为在同一个块中的复杂度怎么搞都是sqrt(n)级别的,就算是跨越的块的区间因为r是排过序的,复杂度也不会太高。

分块写比较简单,先拍了。。至于先建树那种搞法下次有机会再敲吧。

#define _CRT_SECURE_NO_DEPRECATE
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;

typedef long long LL;

const LL MOD = 1e9 + 7;

//乘法逆元
LL ex_gcd(LL a, LL b, LL &x, LL &y)
{
	if (a == 0 && b == 0) return -1;
	if (b == 0) { x = 1; y = 0; return a; }
	LL d = ex_gcd(b, a%b, y, x);
	y -= a / b*x;
	return d;
}
LL Get_inv(LL a, LL n = MOD)
{
	LL x, y, d = ex_gcd(a, n, x, y);
	if (d == 1) return (x%n + n) % n;
	else return -1;
}

const int maxn = 3e4 + 10;

LL nowcnt[maxn], a[maxn], n, m;
LL ans[maxn], unit_size, inv[maxn];

struct Q {
	int l, r, id;
	bool operator < (const Q &x) const {
		if (l / unit_size == x.l / unit_size) return r < x.r;
		return l / unit_size < x.l / unit_size;
	}
};

Q q[maxn];

void gao() {
	LL nowl = q[1].l, nowr = q[1].r;
	LL nowval = 1, nowc = 0;
	for (int i = nowl; i <= nowr; i++) {
		nowc++; nowcnt[a[i]]++;
		nowval = (nowval * nowc) % MOD;
		nowval = (nowval * inv[nowcnt[a[i]]]) % MOD;
	}
	ans[q[1].id] = nowval;
	for (int i = 2; i <= m; i++) {
		while (nowl > q[i].l) {
			nowl--; nowc++;
			nowcnt[a[nowl]]++;
			nowval = (nowval * nowc) % MOD;
			nowval = (nowval * inv[nowcnt[a[nowl]]]) % MOD;
		}
		while (nowr < q[i].r) {
			nowr++; nowc++;
			nowcnt[a[nowr]]++;
			nowval = (nowval * nowc) % MOD;
			nowval = (nowval * inv[nowcnt[a[nowr]]]) % MOD;
		}
		while (nowr > q[i].r) {
			nowval = (nowval * inv[nowc]) % MOD;
			nowval = (nowval * nowcnt[a[nowr]]) % MOD;
			nowcnt[a[nowr]]--;
			nowr--; nowc--;
		}
		while (nowl < q[i].l) {
			nowval = (nowval * inv[nowc]) % MOD;
			nowval = (nowval * nowcnt[a[nowl]]) % MOD;
			nowcnt[a[nowl]]--;
			nowl++; nowc--;
		}
		ans[q[i].id] = nowval;
	}
}

int main() {
	for (int i = 1; i <= 30000; i++) inv[i] = Get_inv(i) % MOD;
	int T; scanf("%d", &T);
	while (T--) {
		memset(nowcnt, 0, sizeof(nowcnt));
		scanf("%d%d", &n, &m);
		for (int i = 1; i <= n; i++) {
			scanf("%d", &a[i]);
		}
		for (int i = 1; i <= m; i++) {
			scanf("%d%d", &q[i].l, &q[i].r);
			q[i].id = i;
		}
		unit_size = (int)sqrt((double)(n));
		if (unit_size <= 0) unit_size = 1;
		sort(q + 1, q + 1 + m);
		gao();
		for (int i = 1; i <= m; i++) {
			printf("%I64d\n", ans[i]);
		}
	}
	return 0;
}

  

时间: 2024-10-29 17:56:24

HDU 5145 NPY and girls 莫队算法的相关文章

hdu 5145 NPY and girls 莫队

题意: 给你1-n属于的班级 给你一个[l,r]区间 问你如果要访问这个区间内所有的女生 有多少种走不同教室的方法 思路: 和小z差不多 只不过这个维护的是阶乘 找出来公式之后莫队直接离线处理 莫队更多的是离线排序优化的思想 把所有查询排序处理 然后逐个处理 可以应用到很多方面 1 #include<bits/stdc++.h> 2 #define cl(a,b) memset(a,b,sizeof(a)) 3 #define debug cerr<<#a<<"

HDU 5145 NPY and girls(莫队算法+乘法逆元)

[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=5145 [题目大意] 给出一个数列,每次求一个区间数字的非重排列数量.答案对1e9+7取模. [题解] 我们发现每次往里加入一个新的数字或者减去一个新的数字,前后的排列数目是可以通过乘除转移的,所以自然想到用莫队算法处理.因为答案要求取模,所以在用除法的时候要计算逆元. [代码] #include <cstdio> #include <algorithm> #include <

Hdu5145NPY and girls莫队算法

Problem Description NPY's girlfriend blew him out!His honey doesn't love him any more!However, he has so many girlfriend candidates.Because there are too many girls and for the convenience of management, NPY numbered the girls from 1 to n.These girls

HDU 5145 NPY and girls (莫队分块离线)

题目地址:HDU 5145 莫队真的好神奇..这样的复杂度居然只有n*sqrt(n)... 裸的莫队分块,先离线,然后按左端点分块,按块数作为第一关键字排序,然后按r值作为第二关键字进行排序.都是从小到大,可以证明这样的复杂度只有n*sqrt(n).然后进行块之间的转移. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <

hdu NPY and girls 莫队+逆元

NPY and girls Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Problem Description NPY's girlfriend blew him out!His honey doesn't love him any more!However, he has so many girlfriend candidates.Because there are to

HDU 6278 - Just h-index - [莫队算法+树状数组+二分][2018JSCPC江苏省赛C题]

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6278 Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 132768/132768 K (Java/Others) Problem Description The h-index of an author is the largest h where he has at least h papers with citations not les

(预处理+莫队算法)HDU - 5381 The sum of gcd

题意: 一个长度为n的数列,m次查询L到R之间所有连续子序列的gcd之和. 分析: 很明显的莫队算法. 很明显发现了gcd是单调递减的,并且最多存在32个的性质. 想了很久,脑补了许多种方法来拉伸L和R,但是都有漏洞. 实际上,这道题还是比较复杂的.. 在思考的过程中,我没有充分利用gcd的递减性质. 这题其实这题有共通之处,至少在我的做法上是这样的. 可以发现,在R向右拉伸的过程中,增加的和只是从L到R+1中的每一个后缀的和. 向左则为减,L的移动同理. 那么我们只要提前预处理每个位置的前缀所

hdu 5273 Dylans loves sequence(区间逆序对数-莫队算法)

n<=1000,q<=100000,求区间内逆序对数,从[l,r]显然可以log(n)的时间内移动到[l-1,r],[l+1,r],[l,r-1],[l,r+1],那么就可以用莫队进行离线 复杂度大概是O(n*sqrt(n)*log2(n)),不过可以暴力枚举起点,然后向后统计,然后O(1)回答,不过n再大就无法解决了,这个即使是n<=1e5也可以很快得到答案,开-o优化,1e6也可以很快得到答案 #include<bits/stdc++.h> using namespace

hdu5381 The sum of gcd]莫队算法

题意:http://acm.hdu.edu.cn/showproblem.php?pid=5381 思路:这个题属于没有修改的区间查询问题,可以用莫队算法来做.首先预处理出每个点以它为起点向左和向右连续一段的gcd发生变化的每个位置,不难发现对每个点A[i],这样的位置最多logA[i]个,这可以利用ST表用nlognlogA[i]的时间预处理,然后用二分+RMQ在nlogn的时间内得到.然后就是区间变化为1时的转移了,不难发现区间变化为1时,变化的答案仅仅是以变化的那一个点作为左端点或右端点的