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 ot= 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
", ans);
	}
	
	return 0;
}
原文地址:https://www.cnblogs.com/czhui666/p/13991427.html