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;
}