hdu6677 度度熊与组题 2019百度之星初赛第二场

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6677

度度熊与组题

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 86    Accepted Submission(s): 34

Problem Description

沃老师在出比赛的题目时遇到麻烦啦!
遇到的麻烦如下:
现在沃老师手上有 2n 道题,题目编号由 1∼2n,已知第 i 道题难度为 ai,这些题的难度还满足当 i<j 时 ai≤aj。现在沃老师想把这些题目分在两套比赛上,每套比赛会被分到 n 道题,每道题都会恰出现在其中一场比赛中。假设分配完后,第一套题难度第 i 小的题的难度为 ci (第 i 小是指不去重的的第 i小,例如有四道题难度分别是 1,1,2,3 时,难度第 3 小的题是难度是 2),第二套题难度第 i 小的题为 di,沃老师定义两场比赛的难度相似度为 ∑ni=1|ci−di|,且沃老师希望分配完题目后,两场比赛的难度相似度尽可能的小。
看到这你可能会觉得这算什么麻烦,难度相似度的最小值不就是 ∑ni=1(a2i−a2i−1) 嘛?
是的,光是要使难度相似度最小并不构成沃老师的麻烦,但沃老师是个好奇宝宝,他还想知道,有多少种分配题目的方式,能使难度相似度最小呢?这个问题可能就没那么简单了。
于是沃老师就来拜托聪明的度熊帮他解决他心中的困惑,各位也帮忙算算吧。
请输出分配题目的方式数量除以 109+7 的余数。
两种分配方式 A 和 B 不同当且仅当存在某道题出现在 A 的第一套比赛中,却没有出现在 B 的第一套比赛中。举例来说,当 n=1 时,第一套比赛含有题目 1 第二套比赛含有题目 2 和 第一套比赛含有题目 2 第二套比赛含有题目 1 是不同的。

Input

有多组询问,第一行包含一个正整数 T 代表有几组询问,接着每组测试数据占 2 行,第一行包含一个正整数 N,第二行包含 2N 个正整数 a1,a2,…,a2N。

* 1≤T≤2×104
* 1≤N≤105
* 1≤ai≤2×N
* 对于不同的正整数 i,j,若 i<j,则 ai≤aj
* 所有询问的 N 的总和不超过 106

Output

对于每一个询问。输出一个非负整数代表答案除以 109+7 的余数。

Sample Input

5 2 1 2 2 4 2 1 2 3 4 1 1 1 1 1 2 6 9 9 9 10 10 10 11 11 11 12 12 12

Sample Output

6 4 2 2 324 Note 令 day1 是第一套比赛的题目题号集合,day2 是第二套比赛的题目题号集合。 在第一组询问中,全部六种组合难度相似度都是 $3$,故答案为 $6$。 在第二组询问中难度相似度最小为 $2$,有四种可能分配方式如下: 1. day1:$\{1,3\}$,day2:$\{2,4\}$ 2. day1:$\{1,4\}$,day2:$\{2,3\}$ 3. day1:$\{2,3\}$,day2:$\{1,4\}$ 4. day1:$\{2,4\}$,day2:$\{1,3\}$

Source

2019 年百度之星·程序设计大赛 - 初赛二

 

看了官方题解才做出来的

1)难度<=v-1的题数是偶数,难度<=v的题数是偶数,且前者比后者少2k题

     在2k条中选k条放入一边,剩下的放入另一边

     dp[v] = dp[v-1]*C(2k,k)

2)难度<=v-1的题数是偶数,难度<=v的题数是奇数,且前者比后者少2k+1题

     在2k+1条中选k条或者k+1条放入一边,剩下的放入另一边

     dp[v] = dp[v-1]*( C(2k+1,k)+C(2k+1,k+1) ) = dp[v-1]*C(2k+1,k)*2

3)难度<=v-1的题数是奇数,难度<=v的题数是偶数,且前者比后者少2k+1题

     在2k+1条中选k条放入多的那一边,剩下的放入另一边

     dp[v] = dp[v-1]*C(2k+1,k)

4)难度<=v-1的题数是奇数,难度<=v的题数是奇数,且前者比后者少2k题

     在2k条题目中选k条或者k-1条放入多的那一边

     dp[v] = dp[v-1]*( C(2k,k)+C(2k,k-1) ) = dp[v]=dp[v-1]*C(2k+1,k)

     换一种思路就是在难度<=v-1的题中抽出一个,看做是难度等于v的题,那么当前要放的题目数量为2k+1,在2k+1条题目中选出k条放入一边,剩下的放入另一边,但是不能乘以2,是因为这个2在第二种情况下已经乘过了,再乘就会重复

因为N达到了10^5,所以我们可以先预处理阶乘,再求逆元得出组合数。

C(n,m)=n!/(m!*(n-m)!)=n!*inv(m!)*inv((n-m)!)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 200005
#define MOD 1000000007
#define LL long long
LL fac[N], ans;
// 预处理阶乘 
void init(){
	fac[0]=1;
	for(int i=1;i<=200000;++i) fac[i]=fac[i-1]*i%MOD;
}
// 扩欧求逆元,或者费马小定理
void exgcd(LL a, LL b, LL &x, LL &y){
	if(b==0){
		x=1, y=0;
	}else{
		exgcd(b,a%b,y,x);
		y-=a/b*x;
	}
} 
void cal(int i, int j){
	// n是难度等于v的题目的数量,等于2m或者2m+1 
	int n=i-j, m;
	m=n>>1;
	LL x, y;
	// C(n,m)=n!/(m!*(n-m)!)=n!*inv(m!)*inv((n-m)!)
	if((i&1)==0&&(j&1)==0){       // 偶偶 
		exgcd(fac[m],MOD,x,y);    // x=inv(m!) 
		ans=ans*fac[n]%MOD*x%MOD*x%MOD;
	}else if((i&1)==1&&(j&1)==0){ // 偶奇 
		exgcd(fac[m],MOD,x,y);
		ans=ans*fac[n]%MOD*x%MOD;
		exgcd(fac[m+1],MOD,x,y);
		ans=(ans<<1)*x%MOD;
	}else if((i&1)==0&&(j&1)==1){  // 奇偶
		exgcd(fac[m],MOD,x,y);
		ans=ans*fac[n]%MOD*x%MOD;
		exgcd(fac[m+1],MOD,x,y);
		ans=ans*x%MOD;
	}else{                         // 奇奇
		exgcd(fac[m],MOD,x,y);
		ans=ans*fac[n+1]%MOD*x%MOD;
		exgcd(fac[m+1],MOD,x,y);
		ans=ans*x%MOD;
	}
}
int main()
{
	init();
	int t, n, m, i, now, pre, num;
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		n<<=1; // 共有2n个数 
		num=0; // 难度<=v-1的题目的数量
		ans=1; // dp
		scanf("%d",&pre); // 输入第一个数 
		for(i=2;i<=n;++i){
			// 输入当前数 
			scanf("%d",&now);
			// 和上一个数进行比较 
			if(now!=pre){
				// 如果不相等,那么指针向前退一个 
				i--;
				// num为难度<=v-1的题目的数量
				//  i 为难度<=v的题目的数量
				cal(i,num);
				// 更新num和pre 
				num=i;
				pre=now;
				// 恢复i 
				i++;
			}
		}
		// 计算最后一组题目 
		i--;
		cal(i,num);
		printf("%lld\n",(ans%MOD+MOD)%MOD);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值