hdu5145 (莫队+逆元)

题意:拜访n个女生,每个女生属于一个班级,每次只能访问一个班的一个女生,问访问所有女生有多少种顺序(顺序指的是班级顺序)

假设有n个女生,分别属于m个班级,每个班级有num(i)个女生,那么访问的种数就是n!/(num1!*num2!*...numm!),

假设区间缩小,第m个班女生少了一个的话结果就是原来的结果乘上numm / n 这里涉及到除法需要用到逆元 (a/b) % p == a*b^-1%p, b^-1次方是b相对于p的逆元,利用费马定理求得b相对于p的逆元是b^p-2,快速幂跑一下就好了

 

这题有个坑点有点不解:

            while (L < Q[i].l) del(L),L++;
            while (L > Q[i].l) --L,add(L);

            while (R < Q[i].r) ++R,add(R);
            while (R > Q[i].r) del(R),R--;
            

如果我while的顺序按上面这么写的话就会WA,必须把两个R循环放在L循环上, 原本是想add函数里有用到(R-L+1)如果L循环放在上面的话R-L+1可能会出现负数,但是我把L循环放上面然后在(R-L+1)这里加上绝对值还是WA, 有点蒙

 

随机数大法好。。。写了个程序造数据跑了一下知道原因了,如果我把L循环放到R循环之上,那么先是L->Q[i].l然后再R->Q[i].r因为Q[i].r >= Q[i].l那么在R->Q[i].r的过程中一定会出现R+1==L的时候,也就是R-L+1 == 0,这时候加了绝对值也还是0,一乘,结果就变成零了

 

再更新一下说明为什么这样写样例能过,样例的查询左端点都是1,而初始的L也是1那么要使R-L+1==0也就是使R-1+1==0,只有当R==0时才成立,而我while (R < Q[i].r) ++R,add(R)这里++R是写在add前面的,那么就不存在R==0使得R-L+1==0,结果也就不会等于0了,你说巧不巧。。。

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

using namespace std;
typedef long long ll;
const int maxn = 3e4+5;
const int mod = 1000000007;
ll a[maxn],pos[maxn],cnt[maxn],n,m;
int L = 1, R = 0;
ll Ans, ans[maxn];
ll ni[maxn];

struct node
{
	int l,r,id;
}Q[maxn];

int cmp(node a, node b)
{
	if (pos[a.l] == pos[b.l])
		return a.r < b.r;
	return pos[a.l] < pos[b.l];
}

ll calc(ll a,ll b)
{
	ll ans = 1;
	while (b > 0)
	{
		if (b & 1)
			ans = ans * a % mod;
		a = a * a % mod;
		b /= 2;
	}
	return ans;
}

void add(int x)
{
	cnt[a[x]]++;
	Ans = Ans * (R-L+1) % mod;
	Ans = Ans * ni[cnt[a[x]]] % mod;
}

void del(int x)
{
	Ans = Ans * cnt[a[x]] % mod;
	Ans = Ans * ni[R-L+1] % mod;
	cnt[a[x]]--;
}

int main()
{
	for (int i = 1; i <= 30000; i++)
		ni[i] = calc(i,mod-2);
	ni[0] = 1;
	int T;
	scanf("%d",&T);
	
	while (T--)
	{
		memset(cnt,0,sizeof(cnt));
		scanf("%d%d",&n,&m);
		int sz = sqrt(n);
		for (int i = 1; i <= n; i++)
		{
			scanf("%lld",&a[i]);
			pos[i] = i / sz;
		}
		for (int i = 1; i <= m; i++)
		{
			scanf("%d%d",&Q[i].l,&Q[i].r);
			Q[i].id = i;
		}
		
		sort(Q+1,Q+1+m,cmp);
		
		L = 1, R = 0, Ans = 1;
		for (int i = 1; i <= m; i++)
		{
			int l = Q[i].l;
			int r = Q[i].r;
			while (R < Q[i].r) ++R,add(R);
			while (R > Q[i].r) del(R),R--;
			while (L < Q[i].l) del(L),L++;
			while (L > Q[i].l) --L,add(L);
			
			ans[Q[i].id] = Ans;
		}
		for (int i = 1; i <= m; i++)
			printf("%lld\n",ans[i]);
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值