1029考试总结

1029考试总结

T2

​ 题目大意:

​ 给定一个数\(n\),\(n <= 10 ^ {100000}\),输出一个比\(n\)大的最小的一个合法数."合法数"的定义是:只有4,7两个数字,并且4,7的个数相等.

​ 贪心

​ 设\(n\)的位数是\(len\), 若\(len\)为奇数,那么答案的长度一定为\(len + 1\),并且是4444477777这种的.偶数的话,答案的长度可能为\(len\),可能为\(len + 2\).

​ 长度为\(len\)的直接一位一位的考虑,记录一下4,7剩余几个能用,优先填入最小的.

#include <bits/stdc++.h>

using namespace std;

char ch[100005], ans[100005];
int len;

int dfs(int now, int n4, int n7, int tag) {
	if(now == len) return 1;
	if(tag) {
		for(int i = 0;i < n4; i++) ans[i + now] = '4';
		for(int i = 0;i < n7; i++) ans[i + now + n4] = '7';
		return 1;
	}
	if(ch[now] <= '4' && n4 && dfs(now + 1, n4 - 1, n7, ch[now] < '4')) {
		ans[now] = '4'; return 1;
	}
	if(ch[now] <= '7' && n7 && dfs(now + 1, n4, n7 - 1, ch[now] < '7')) {
		ans[now] = '7'; return 1;
	}
	return 0;
}

int main() {
	
	while(cin >> ch) {
		len = strlen(ch);
		if((len & 1) || !dfs(0, len / 2, len / 2, 0)) {
			len += 1 + (len % 2 == 0);
			for(int i = 0;i < len / 2; i++) ans[i] = '4';
			for(int i = len / 2;i < len; i++) ans[i] = '7';
		}
		for(int i = 0;i < len; i++) cout << ans[i];
		cout << "\n";
	}
	
	return 0;
}

T3

​ 题目大意:

​ 给定一个\(n * m\)的矩阵,往矩阵里面填\(1\)~\(k\)里的数,规定任意一条从左上角到右下角的路径都不能有重复的数字,这里的路径只可以往下或往右走.有些格子里一开始就有数,不能改变,问有多少种填数方案.\(n, m, k <= 10\)答案对\(1e9 + 7\)取模.

​ 缩索.

​ 加两个剪枝就可以过了.

​ 如果\(n + m - 1 > k\),说明必定有一条路径会有重复的数字(抽屉原理),那么直接返回0;

​ 如果某个位置放的颜色第一次出现,那么这些方案数都相同,只需要搜一次即可.(题解原话)

#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 = 30, mod = 1e9 + 7;
int n, m, k, ans;
int a[N][N], d[1 << 18], num[N], f[N][N];

int dfs(int x, int y) {
	if(y == m + 1) x ++, y = 1;
	if(x == n + 1) return 1; 
	int now = f[x - 1][y] | f[x][y - 1], tmp = now, c = 0, tag = -1, res = 0;
	while(tmp) { if(tmp & 1) c ++; tmp >>= 1; }
	if(n - x + m - y + 1 > k - c) return 0;
	for(int i = 1;i <= k; i++) {
		if(now & (1 << (i - 1))) continue;
		tmp = (1 << (i - 1));
		if(!a[x][y] || a[x][y] == i) {
			num[i] ++;
			f[x][y] = now | tmp;
			if(num[i] == 1) {
				if(tag == -1) tag = dfs(x, y + 1);
				res = (res + tag) % mod;
			}
			else if(num[i]) res = (res + dfs(x, y + 1)) % mod;
			num[i] --;
		}
	}
	return res;
}

int main() {
	
	n = read(); m = read(); k = read();
	if(n + m - 1 > k) { printf("0"); return 0; }
	for(int i = 0;i <= 16; i++) d[1 << i] = i + 1;
	for(int i = 1;i <= n; i++) 
		for(int j = 1;j <= m; j++) {
			a[i][j] = read(); 
			if(a[i][j]) num[a[i][j]] ++;
		}
			
	printf("%d", dfs(1, 1));

	return 0;
}

T4

​ 题目大意:

​ 两只狗在大街上发现了T大块肉。对于每一块肉,甲狗和乙狗把肉一扯开,甲狗会得n千克的肉,乙狗得m千克的肉,少肉的那条狗会抢对方的肉,使自己的肉多一倍。抢了k次后两条狗都累了,那么此时少肉的一方剩下多少千克的肉呢?(注:若两狗的肉一样多,甲狗会主动抢乙狗的肉)

\(n, m, k <= 1e9, T <= 10\).

​ 快速幂.

​ 可以发现,每次\(n, m\)都是\(\times 2\)%\((n + m)\).

​ 如果\(n >= m\), 说明\(2n >= n + m >= 2m\),模\((n + m)\)后就变成了\(n - m, 2m\),这其实就是乙狗强烈甲狗的肉,\(n < m\)就同理.

​ 所以直接用快速幂优化乘\(k\)次二就好了.

#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 n, m, k;

int ksm(int x, int y, int mod) {
	int res = 1;
	while(y) {
		if(y & 1) res = 1ll * res * x % mod; 
		x = 1ll * x * x % mod; y >>= 1;
	} 
	return res;
}

int main() {
	
	for(int t = read(); t ; t--) {
		n = read(); m = read(); k = read();
		int tmp = ksm(2, k, n + m);
		printf("%lld\n", min(1ll * n * tmp % (n + m), 1ll * m * tmp % (n + m)));
	}
    
	return 0;
}
posted @ 2020-10-29 21:47  C锥  阅读(180)  评论(0编辑  收藏  举报