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;
}
posted @ 2020-11-16 21:49  C锥  阅读(74)  评论(0编辑  收藏  举报