1115上午考试
1115上午考试总结
T1
题目大意:
如果一个数字里有有连续的 1 个1,或者有连续的 2 个 2,或者有连续的 3 个 3,或者有连续的 4 个 4,或者有连续的 5 个 5,或者有连续的 6 个 6,或者有连续的 7 个 7,或者有连续的 8 个 8,或者有连续的 9 个 9,则这个数字是 good number。在所有 N 位数中有多少个 good number?
这个数字可能有点大,所以对 1e9 + 7 取模就好了。\(n <= 1000\)
数位DP.
\(f[i][j][k]\)表示前\(i\)位数字最后放连续的\(k\)个数字\(j\)的不是\(good \ number\)的个数.
首先可以知道, 要想不是合法数字, 这里面肯定不能出现1.
上一位为0需要特殊判断, 这一位除了1都可以放, 那么就是\(f[i][j][1] += f[i - 1][0][1](j != 1)\).
上一位与这一位相同, 那么得让这一位的数字\(j\)连续的个数小于\(j\), 就是\(f[i][j][k] += f[i - 1][j][k - 1] (k < j)\).
上一位与这一位不同, 就是\(f[i][u][1] += f[i - 1][j][k] \ (u != j \& k < j)\).
最后统计答案我们要用总的数的个数\((10 ^ n - 10 ^ {n - 1})\)减去\(f[n][i][j] \ (i == 0 || i < j)\).
#include <bits/stdc++.h>
using namespace std;
const int N = 3005, mod = 1e9 + 7;
int n, f[N][10][10];
int ksm(int x, int y) {
int res = 1;
while(y) {
if(y & 1) res = 1ll * res * x % mod;
x = 1ll * x * x % mod; y >>= 1;
}
return res;
}
int main() {
cin >> n;
for(int i = 2;i <= 9; i++) f[1][i][1] = 1;
for(int i = 2;i <= n; i++) {
for(int j = 0;j <= 9; j++) {
if(j == 1) continue;
f[i][j][1] = (f[i][j][1] + f[i - 1][0][1]) % mod;
}
for(int j = 2;j <= 9; j++) {
for(int k = 1;k < j; k++) {
f[i][j][k] = (f[i][j][k] + f[i - 1][j][k - 1]) % mod;
for(int u = 0;u <= 9; u++)
if(k < j && u != j) f[i][u][1] = (f[i][u][1] + f[i - 1][j][k]) % mod;
}
}
}
int sum = (ksm(10, n) - ksm(10, n - 1) + mod) % mod;
for(int i = 0;i <= 9; i++)
for(int j = 0;j <= 9; j++)
if(i == 0 || j < i)
sum = (sum - f[n][i][j] + mod) % mod;
printf("%d", sum);
return 0;
}
T2
题目大意:
在 1 到 \(A^n\) 中满足所包含数字不重复(A进制下)且为 k 倍数的数有多少个?\(A <= 11, n <= 1e9, k <= 1e9\)
乱搞.
首先我们可以知道\(n = min(A, n)\),因为如果\(n > A\)那么至少会有两位的数字重复(\(A\)进制下), 抽屉原理.
然后我们乱搞一下.
当\(k\)比较大的时候, 我们直接枚举\(k\)的倍数, 然后看一看是否有重复的数字.
当\(k\)比较小的时候, 我们可以用数位DP.
\(f[i][j]\)表示状态为\(i\)时, 这个数模\(k\)后的到的余数为\(j\)的数字有多少个.对于一个状态\(i\)如果说第\(i\)位为1, 那么说明这个数字内存在\(i\)这个数字\(0 <= i <= A - 1\).
然后转移就可以了, \(f[i | (1 << j)][mod * A + j] += f[i][mod]\).
复杂度玄学, 但加上优化可以跑得飞快.
#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 A, n, k, ans;
int f[2049][40005], cnt[2049], w[12];
void work1() {
int S = (1 << A), ss = min(A, n); f[0][0] = 1;
for(int i = 1;i < S; i++) cnt[i] = cnt[i >> 1] + (i & 1);
for(int i = 0;i < S; i++)
for(int j = 0;j < k; j++)
if(f[i][j] && cnt[i] < ss) {
int s = (i == 0) ? 1 : 0;
for(int l = s;l < A; l++) {
if(!(i & (1 << l))) f[i | (1 << l)][(j * A + l) % k] += f[i][j];
}
}
for(int i = 1;i < S; i++) ans += f[i][0];
printf("%d", ans);
}
int judge(long long x) {
for(int i = 0;i <= 11; i++) w[i] = 0;
while(x) { w[x % A] ++; x /= A; }
for(int i = 0;i <= 11; i++) if(w[i] > 1) return 0;
return 1;
}
void work2() {
long long S = pow(A, n);
for(int i = 1;1ll * k * i <= S; i++) {
if(judge(1ll * i * k)) ans ++;
}
printf("%d", ans);
}
int main() {
A = read(); n = read(); k = read();
n = min(n, A);
if(k <= 40000) work1();
else work2();
return 0;
}
T3
题目大意:
一颗\(n\)个节点\(m\)条边的有向图, 每条边有一个权值\(w[i]\),给定一个起点\(s\), 求\(\displaystyle \sum_{s \not= t} c(t)\).\(c(t)\)表示从\(s\)到\(t\)的路径上边权最小值的最大值.
\(n <= 2e5, m <= 2e6, w <= 2e6\).
最短路.
\(dis[i]\)表示从\(s\)到\(i\)的路径上边权最小值的最大值.类似于dij, 我们每次找到一个\(dis\)最大的点取更新其他点.
假设当前从\(x\)经过边\(val\)到\(y\), 那么\(dis[y] = max(dis[y], min(dis[x], val))\). 复杂度\(O(m log n)\).
但是过不了, 怎么办?
我们可以发现, 按照这个更新顺序\(dis\)是单调不升的. 所以我们可以搞一个指针, 在搞一个桶.
\(v[w][...]\),表示当前\(dis\)为\(w\)的点有哪些.
然后我们让指针枚举边权, 从大到小走, 如果说当前\(v[w]\)不为空, 那么我们就可以用这个点去更新别的点.复杂度\(O(m)\).
#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;
}
const int N = 2e5 + 5, M = 2e6 + 5, mod = 998244353;
int n, m, s, c0, c1, c2, c3, k, cnt;
long long ans;
int get_x(int i) {
int t = 3 * i + 1;
return (((1ll * c3 * t % mod * t % mod * t % mod + 1ll * c2 * t % mod * t % mod) % mod + 1ll * c1 * t % mod) % mod + c0) % mod % n + 1;
}
int get_y(int i) {
int t = 3 * i + 2;
return (((1ll * c3 * t % mod * t % mod * t % mod + 1ll * c2 * t % mod * t % mod) % mod + 1ll * c1 * t % mod) % mod + c0) % mod % n + 1;
}
int get_z(int i) {
int t = 3 * i + 3;
return (((1ll * c3 * t % mod * t % mod * t % mod + 1ll * c2 * t % mod * t % mod) % mod + 1ll * c1 * t % mod) % mod + c0) % mod % k + 1;
}
//以上是生成图的公式, 题目给定的.
const int inf = 1e9;
int dis[N], vis[N], head[N];
vector <int> v[2000005];
struct edge { int to, nxt, val; } e[M];
void add(int x, int y, int z) {
e[++ cnt].nxt = head[x]; head[x] = cnt; e[cnt].to = y; e[cnt].val = z;
}
void clear() {
for(int i = 1;i <= n; i++) head[i] = 0, vis[i] = 0; cnt = ans = 0;
for(int i = 0;i <= k; i++) v[i].clear();
}
void bfs() {
int maxn = 0;
for(int i = 1;i <= n; i++) dis[i] = -inf; dis[s] = inf;
for(int i = head[s]; i ; i = e[i].nxt) {
int y = e[i].to;
if(dis[y] < min(dis[s], e[i].val))
dis[y] = min(dis[s], e[i].val), v[dis[y]].push_back(y), maxn = max(maxn, dis[y]);
}
vis[s] = 1;
for(int k = maxn; k >= 0 ; k --) {
if((int) v[k].size()) {
for(int i = 0;i < (int)v[k].size(); i++) {
int x = v[k][i]; if(vis[x]) continue; vis[x] = 1;
for(int j = head[x]; j ; j = e[j].nxt) {
int y = e[j].to;
if(dis[y] < min(dis[x], e[j].val))
dis[y] = min(dis[x], e[j].val), v[dis[y]].push_back(y);
}
}
}
}
}
int main() {
for(int T = read(); T ; T --) {
n = read(); m = read(); s = read();
c0 = read(); c1 = read(); c2 = read(); c3 = read(); k = read();
clear();
for(int i = 1, x, y, z;i <= m; i++) {
x = get_x(i); y = get_y(i); z = get_z(i);
add(x, y, z);
}
bfs();
for(int i = 1;i <= n; i++) if(i != s) ans += (dis[i] == -inf ? 0 : dis[i]);
printf("%lld\n", ans);
}
return 0;
}