HDU 6044& 2017年多校训练第一场 1012题

多校训练第一场1022题

Problem Description
As to a permutation   from   to  , it is uncomplicated for each   to calculate   meeting the condition that   if and only if   for each  .

Given the positive integers    , you are asked to calculate the number of possible permutations   from   to  , meeting the above condition.

The answer may be very large, so you only need to give the value of answer modulo  .
 

Input
The input contains multiple test cases.

For each test case:

The first line contains one positive integer  , satisfying  .

The second line contains   positive integers  , satisfying   for each  .

The third line contains   positive integers  , satisfying   for each  .

It's guaranteed that the sum of   in all test cases is not larger than  .

Warm Tips for C/C++: input data is so large (about 38 MiB) that we recommend to use  fread() for buffering friendly.
size_t fread(void *buffer, size_t size, size_t count, FILE *stream); // reads an array of count elements, each one with a size of size bytes, from the stream and stores them in the block of memory specified by buffer; the total number of elements successfully read is returned.
 

Output
For each test case, output " Case # " in one line (without quotes), where   indicates the case number starting from   and   denotes the answer of corresponding case.
 

Sample Input
  
  
3 1 1 3 1 3 3 5 1 2 2 4 5 5 2 5 5 5
 

Sample Output
  
  
Case #1: 2 Case #2: 3


欸,直接复制连格式好像都复制过来了,真好。

题目意思大致是说:对于每一个数都会给你一个区间,并且当前数是给定区间内最小的,在这种情况下让你求可能的组合数。

哇,感觉这么说好像不太能理解,那就直接上图吧:(用第二个样例来举例)


如果我们稍微改动一下:


是不是忽然感觉发现了什么?

对于1,它是整个区间的最小,所以是固定了的0.

对于3,在1之后是剩余区间最小,所以是1.

对于2,在1 3之后剩余区间内的某一个长度为1的区间最小

对于4,在1 3之后剩余区间内的某一个长度为2的区间最小

对于5,在4的区间内去掉4的值之后 固定的最小。

所以样例的答案3 来自于对2 和4的区间的分配。


所以题目就好理解了。如果能够用类似的方法建出树,然后就可以在子树合并的时候,用组合数来求了。涉及到的组合数比较大,大概在10W范围内,所以要预处理出阶乘和阶乘的逆元。

O(N)求 1 - N 的逆元:

void init() {
	inv[1] = 1;
	for(int i = 2; i < maxn; i++) {
		inv[i] = (mod - mod / i) * inv[mod % i] % mod;
	}
}


题目到这里,好像大概都清楚了。在比赛的时候太菜,最后半小时想到这个东西,当时想的建树可以用拓扑排序的方法来,然后删边什么的就好。但是越想越奇怪。当时想到了一个错误的方向,关于题目,比如说样例

5

1 2 1 4 5

1 2 5 4 5

这种情况该怎么弄。子树如果不是只有最多两颗的情况怎么办?

然后大佬给了我解答: if and only if   for each  .

对不起,我读题有误,我的锅。也就是说建树只有可能是左右两个子树。用官方题解的话,一开始必然存在一个区间覆盖(1, n),这个节点 i 作为父节点,必然存在节点覆盖(i.left, i - 1), (i + 1, i.right)。所以甚至不想建树的话,可以直接递归合并。

到这里差不多就讲完了。不过,如果仅仅是这样,交上去肯定是T了的。题目中也写出要输入优化,所以要输入优化。

还有就是如果这样直接递归,会有爆栈的风险(最后几个样例boom boom boom),所以要加个黑科技:(网上搜的)

#pragma comment(linker, /STACK:102400000,102400000)
这个能打开HDU的栈限制,然就开开心心的不爆栈了。


哦,对了。除了官方提供的加速输入,我也试了一下OI里常有的快速读入,但是快速读入效果不好,直接T了,贴在代码里,有兴趣的同学就别试了。说多了都是泪


代码如下:

#pragma comment(linker, /STACK:102400000,102400000)
#include <bits/stdc++.h>

namespace fastIO {
	#define BUF_SIZE 100000
	//fread -> read
	bool IOerror = 0;
	inline char nc() {
		static char buf[BUF_SIZE], *p1 = buf + BUF_SIZE, *pend = buf + BUF_SIZE;
		if(p1 == pend) {
			p1 = buf;
			pend = buf + fread(buf, 1, BUF_SIZE, stdin);
			if(pend == p1) {
				IOerror = 1;
				return -1;
			}
		}
		return *p1++;
	}
	inline bool blank(char ch) {
		return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t';
	}
	inline void read(int &x) {
		char ch;
		while(blank(ch = nc()));
		if(IOerror)
			return;
		for(x = ch - '0'; (ch = nc()) >= '0' && ch <= '9'; x = x * 10 + ch - '0');
	}
	#undef BUF_SIZE
};
using namespace fastIO;

/*
inline bool reand(int & ret) {
    char c; int sgn;
    if(c = getchar(), c == EOF) return 0;
    while(c != '-' && (c < '0' || c > '9')) c = getchar();
    sgn = (c == '-') ? -1 : 1;
    ret = (c == '-') ? 0 : (c - '0');
    while(c = getchar(), c >= '0' && c <= '9') ret = ret * 10 + (c - '0');
    ret *= sgn;
    return 1;
}
*/


typedef long long ll;
const ll mod = 1e9 + 7;
const int maxn = 1e6 + 500;
ll ans;

ll inv[maxn], ni[maxn], num[maxn];
void init() {
	inv[1] = 1;
	ni[0] = ni[1] = inv[1];
	num[0] = num[1] = 1;
	for(int i = 2; i < maxn; i++) {
		num[i] = num[i - 1] * i % mod;
		inv[i] = (mod - mod / i) * inv[mod % i] % mod;
		ni[i] = ni[i - 1] * inv[i] % mod;
	}
}
ll getC(int n, int m) {
	return num[n] * ni[m] % mod * ni[n - m] % mod;
}

struct Node{
    int l, r, id, len;
}node[maxn];
std::vector<Node> mp[maxn];

int cmp(Node a, Node b) {
    if(a.l == b.l) return a.len > b.len;
    return a.l < b.l;
}

int binary(int p, int len) {
    int l = 0, r = mp[p].size() - 1;
    while(l <= r) {
        int m = (l + r) >> 1;
        if(mp[p][m].len == len) return m;
        else if(mp[p][m].len > len) l = m + 1;
        else r = m - 1;
    }
    return -1;
}

bool solve(int p, int len) {
    //printf("%d - %d\n", p, len);
    if(len == 0) return true;
    int pos = binary(p, len);
    //printf("----------%d\n", pos);
    if(pos == -1) return false;
    int id = mp[p][pos].id;
    int l = mp[p][pos].l, r = mp[p][pos].r;
    if(!solve(l, id - l)) return false;
    if(!solve(id + 1, r - id)) return false;
    ans = (ans * getC(r - l, std::min(id - l, r - id))) % mod;
    //printf("Ans: %lld\n", ans);
    return true;
}

int main() {
    //freopen("1012.in", "r", stdin);
    //freopen("out.out", "w", stdout);
    init();
	int kase = 0, n;
	while(read(n), !fastIO::IOerror) {
	//while(reand(n)) {
        for(int i = 1; i <= n; i++) mp[i].clear();
		ans = 1;
		for(int i = 1; i <= n; i++) {
			read(node[i].l);
			//reand(node[i].l);
		}
		for(int i = 1; i <= n; i++) {
			read(node[i].r);
			//reand(node[i].r);
			node[i].id = i;
			node[i].len = node[i].r - node[i].l + 1;
		}
		std::sort(node + 1, node + 1 + n, cmp);
		for(int i = 1; i <= n; i++) mp[node[i].l].push_back(node[i]);
		printf("Case #%d: ", ++kase);
		if(solve(1, n)) printf("%lld\n", ans);
		else puts("0");

	}
	return 0;
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值