1101 考试总结
T1
题目大意:
给定一个不小于2 的整数k ,按照如下方式生成一个无限长的序列S (下标从0 开始)。
-
初始时序列只有一个元素S0 = 0 。
-
对于 j = 1,2,...,k-1分别把当前序列的每个元素都加上 j ,得到新的k -1个序列。
-
把新的k -1个序列依次接在当前序列后面,得到一个长度为当前序列长度k 倍的序列。
-
把这个序列每一项都变成其除以k 之后的余数,并把这个序列作为新的当前序列。
-
执行无穷次操作 2-4。
例如k = 3 ,每一轮执行之后的序列分别是:
012
012 120 201
012120201 120201012 201012120
......
例如k = 2 ,则序列是 01101001100101101001011001101001……
现在给定整数 L, R ,你需要求(displaystyle sum _{i = L} ^{R} h(i) * S_i)的值,并输出答案对(2^{32}) 取模. (h(i) = lfloor frac{(i \% 20000116) ^ 2 + i + 804}{233} floor).
(T <= 100, 2 <= k <= 1000, L,R <= 1e16, sum R - L <= 1e8).
思维题.
我们不考虑模数, 当(k = 3)时候的序列应该就是:0 1 2 1 2 3 2 3 4 ...
然后我们考虑每个数是怎么来的, 我们把序列分层:
0
0 1 2
012 123 234
最后那个4其实是由上一层的2加上2得到的. 可以发现, 每一层的数字可以分层(k)块, 假设一个数字在第(t)块, 那么它就是由上一层对应数字加上(t - 1)得到的.
然后我们只需要找到这个数字对应的第(t)块是什么就好了.代码里的(tmp[i])数组就是表示从第i层向第(i + 1)层需要加多少.
我们先求出(l)的(tmp)数组, 然后推出(l + 1)的(tmp)数组是什么.
怎么推呢?看代码里的(inc)函数.
首先从(tmp[0] ++), 如果说(tmp[0] == k),说明这一层不能继续加了, 就类似于进位一样, 让(tmp[0] = 0, tmp[1] ++).
好吧我还不透彻
#include <bits/stdc++.h>
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
int k, s, cnt, tmp[105];
unsigned ans; //开unsigned自动溢出,就不用取模了
long long l, r;
unsigned h(long long x) {
long long t = x % 20000116;
return (t * t + x + 804) / 233;
}
void inc() {
tmp[0] ++; if(++ s == k) s = 0;
for(int i = 0;tmp[i] == k; i++) {
tmp[i] = 0; tmp[i + 1] ++;
if(++ s == k) s = 0;
}
}
int main() {
for(int T = read(); T ; T --) {
k = read(); l = read(); r = read(); ans = 0; cnt = 0;
memset(tmp, 0, sizeof(tmp));
for(long long i = l; i ; i /= k) tmp[cnt ++] = i % k;
s = 0; for(int i = 0;i < cnt; i++) (s += tmp[i]) %= k;
for(long long i = l;i <= r; i++, inc()) ans += h(i) * s;
printf("%u
", ans);
}
return 0;
}
/*
10
2 1 10
3 1 10
4 1 10
5 1 10
2 1001 5005
10 123 456
233 1024 6174
16 10000 20000
20 12345678 23456789
987 2333123456789 2333198765432
*/
T4
(另一套题的T1)
题目大意:
给定一个(n * m)的棋盘, 然后放两个国际象棋的皇后, 问有多少种方案可以使两个皇后冲突.(n, m <= 1e9),答案对100000007取模.
题目链接
数学, 组合数.
对于每一行, 我们可以放的方案数是(n * A_{m}^{2} = n *m * (m - 1)), 对于每一列, 我们可以放的方案数是(m * A_{n}^{2} = m * n * (n - 1)).
对于每一斜列, 我们可以....???有点难搞.
每条斜线的长度不一样, 我们可以把大的式子写出来(2 * (A_2^2 + A_3^2 + A_4^2+...+ A_n^2 * (m - n + 1) + ... + A_3^2 + A_2^2)).((m >= n), 乘二是因为有左斜线和右斜线)
然后我们把括号拆一下:(4 * (1 * 2 + 2 * 3 + 3 * 4 + ... + (n - 1) * n) + 2n(n - 1)(m - n) = displaystyle 4 *sum_{i = 1}^{n - 1} i(i + 1) + 2n(n - 1)(m - n) = 4 * (sum_{i = 1}^{n - 1}i^2 + sum_{i = 1}^{n - 1}i) + 2n(n - 1)(m - n)).
(displaystyle sum_{i = 1}^{n - 1}i = frac{n(n - 1)}{2}).这个很好理解.那前面那个死个妈怎么搞??
首先:(n ^ 3 - (n - 1)^3 = 3n^2 - 3n + 1, (n - 1)^3 - (n - 2)^3 = 3(n - 1)^2 - 3(n - 1) + 1),这个应该很好推.
然后:(displaystyle sum_{i = 2}^{n} i^3 - (i - 1)^3 = n ^ 3 - 1^3 = 3(n ^ 2 + (n - 1)^2 + ... + 2^2 + 1^2) - 3(n + n - 1 + n - 2 + ... + 2 + 1) + (n - 1))(注意是(n - 1)).
接着移项:(3 * displaystyle sum_{i = 1}^{n} i^2 = n^3 - n + frac{3n(n + 1)}{2}), 所以(displaystyle sum_{i = 1}^{n} i^2 = (n^3 - n + frac{3n(n + 1)}{2})/3) = (frac{2n^3 + 3n^2 + n}{6} = frac{n(n - 1)(2n + 1)}{6}).
所以最后总的式子就是:(n * m * (n - 1 + m - 1) + 2 * n * (n - 1) * (3 * m - n - 1) / 3)
#include <bits/stdc++.h>
using namespace std;
const int mod = 100000007;
long long n, m, inv3;
int ksm(int x, int y) {
int res = 1;
for( ; y ; y >>= 1, x = 1ll * x * x % mod) if(y & 1) res = 1ll * res * x % mod; return res;
}
int main() {
cin >> n >> m; inv3 = ksm(3, mod - 2);
if(n > m) swap(n, m);
printf("%d", (n % mod * m % mod * (n - 1 + m - 1) % mod + 2 * n * (n - 1) % mod * (3 * m - n - 1) % mod * inv3 % mod) % mod);
return 0;
}