2020CCPC绵阳 L-Lottery

CCPC2020-绵阳-重现赛邀请码
dce06e265b24ad49
题目链接:https://pintia.cn/problem-sets/1322796904464203776/problems/1322798545527595019
参考题解:链接
思路
对于一个连续的段,例如样例 1 1 2 1 3 1,那么全部移到首位能够进位的地方,那么这一段的方案数就是该段首位的数字数+1。
光这样做不行,对于有的段,表面上是断开的,实际上是连续的。例如这个样例

3
2 5
4 2
5 1

表面上这里是断开成两部分,其实2的个数多了,这个样例就等价于

4
2 1
3 2
4 2
5 1

这样才是一个完整的段。那么这样就要进行预处理,对于每一个位个数大于2的位数,要把它的个数减少到1或者2,然后把多的数除以2,移到下一位上。对于下一位也这么进行操作一次。
这样预处理完以后所有的段就都是完整的了,可以再进行向前合并,答案就是每一段首位个数+1都乘起来。
代码

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
#define int LL
typedef pair<int, int> PII;
const int mod = 1e9 + 7;
#define gcd __gcd
const int N = 2e5 + 10;

map<int, int> cnt;
int kase;

void solve() {
	cnt.clear();
	printf("Case #%lld: ", ++kase);
	int n;
	scanf("%lld", &n);
	for(int i = 1; i <= n; i++) {
		int a, x;
		scanf("%lld%lld", &a, &x);
		cnt[a] += x;
	}
	
	// 预处理段 
	for(auto it = cnt.begin(); it != cnt.end(); it++) {
		int x = it->first, y = it->second;
		if(y > 2) {
			cnt[x + 1] += (y - 1) / 2;
			cnt[x] = (y & 1) ? 1 : 2;
		}
	}

	auto it = cnt.end(); it--;
	LL res = 1;
	while(it != cnt.begin()) {
		int x = it->first, y = it->second;
		if(!cnt.count(x - 1)) {
			res = res * (y + 1) % mod;
		}
		else {
			cnt[x - 1] += y * 2 % mod;
			cnt[x - 1] %= mod;
		}
		it--;
	}
	int x = it->first, y = it->second;
	res = res * (y + 1) % mod;
	printf("%lld
", res);
}

signed main() {
	// freopen("in.txt", "r", stdin);
	int t; cin >> t; while(t--)
	solve();
	return 0;
}
原文地址:https://www.cnblogs.com/ZX-GO/p/14412982.html