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\n", res);
}
signed main() {
// freopen("in.txt", "r", stdin);
int t; cin >> t; while(t--)
solve();
return 0;
}