传送门
题目大意
现在规定罗马数字只有 个字符:I、V、X、L,分别代表 、 、 、 。规定:一个罗马数字的值为该数字包含的字符代表的数字之和,而与字符的顺序无关(这与实际情况是不一样的!)。
问一个长度为 的罗马数字可以有多少种不同的值。显然答案不会超过 ,所以不对任何数取模。
。
思路
考虑生成函数。最 Naive 的想法是:求
有多少系数非
项,显然不可做。
注意到, ,就是说五个 加上一个 等于一个 加上五个 。如果不存在这个结论,即如果一个长度为 的罗马数字的值只对应一个四元组 ,那么答案显然为 (我靠洛谷垃圾翻译把 L 写成 )。
考虑
或者
。我们规定这种相等的只能取前者,也就是说
和
不能同时成立。那么考虑分
和
两种情况讨论。当
时,剩下三种随便选,对答案的贡献为
。当
时,我们首先枚举
选了多少个,然后枚举
选了多少个,这一部分的方案数为:
然后妥妥地错掉了……那肯定是还有东西算重了,看看还有没有相等的情况。
果然有: ……那么我们同样规定只能出现一侧的值。由于现在有些混乱,因此我们重新做一次……
现在有:
这样好了,我们规定第一个等式的右边必须化为左边,第二个等式的左边必须化为右边,则下面两个式子必须不成立:
那么现在 必须在 到 中选一个,我们用同样的思路去做:当 时,贡献还是 ;当 时,我们就不用考虑等差数列了,直接枚举 和 即可。
然后妥妥地错掉了……那肯定还有东西算重了,看看还有没有相等的情况。
果然有:
……我也是醉了,那就让右边化为左边,这样的话就要满足三个式子始终不成立:
还是枚举这两个,这下跑就对了。
参考代码
我去哦三行代码写了一个上午。
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <cassert>
#include <cctype>
#include <climits>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <set>
#include <bitset>
#include <list>
#include <functional>
typedef long long LL;
typedef unsigned long long ULL;
using std::cin;
using std::cout;
using std::endl;
typedef LL INT_PUT;
INT_PUT readIn()
{
INT_PUT a = 0; bool positive = true;
char ch = getchar();
while (!(ch == '-' || std::isdigit(ch))) ch = getchar();
if (ch == '-') { positive = false; ch = getchar(); }
while (std::isdigit(ch)) { a = a * 10 - (ch - '0'); ch = getchar(); }
return positive ? -a : a;
}
void printOut(INT_PUT x)
{
char buffer[20]; int length = 0;
if (x < 0) putchar('-'); else x = -x;
do buffer[length++] = -(x % 10) + '0'; while (x /= 10);
do putchar(buffer[--length]); while (length);
}
int n;
void run()
{
n = readIn();
LL ans = 0;
for (int i = 0; i <= 8 && i <= n; i++)
for (int j = 0; j <= (i ? 4 : 8) && i + j <= n; j++)
ans += n + 1 - i - j;
printOut(ans);
}
int main()
{
run();
return 0;
}
总结
比赛时肯定打表啊!当 时,答案呈线性增长(观察代码,显然),打表真是轻松……
关键是发现问题后没有及时找到问题(显然是还有算重的情况,但是没有及时找到一组新的)。可能这种问题不是很好避免,所以比赛时最好先写暴力,然后用暴力拍,发现问题后再及时改。不过这样可能时间又不够了……