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 << "
";
}
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)都是( imes 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
", min(1ll * n * tmp % (n + m), 1ll * m * tmp % (n + m)));
}
return 0;
}