zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    MVC<2:路由映射原理2>
    分支限界>0/1背包 小强斋
    分支限界>装载问题 小强斋
    解空间树搜索 及 最优解 小强斋
    算法>NP难度和NP完全的问题 小强斋
    算法>NP难度和NP完全的问题 小强斋
    分支限界>装载问题 小强斋
    分支限界>0/1背包 小强斋
    分支限界>15谜问题 小强斋
    分支限界>15谜问题 小强斋
  • 原文地址:https://www.cnblogs.com/czhui666/p/13991427.html
Copyright © 2011-2022 走看看