A. Password
大水题,求个组合数就水掉了。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define N 1000010
#define ll long long
template <class T>
inline void read(T& a){
T x = 0, s = 1;
char c = getchar();
while(!isdigit(c)){ if(c == '-') s = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + (c ^ '0'); c = getchar(); }
a = x * s;
return ;
}
vector <int> a;
int fac[N];
void prepare(){
fac[0] = fac[1] = 1;
for(int i = 1; i <= 10; i++)
fac[i] = fac[i-1] * i;
return ;
}
int C(int a, int b){
return fac[b] / (fac[a] * fac[b-a]);
}
int main(){
// freopen("hh.txt", "r", stdin);
int T; read(T);
prepare();
while(T--){
// a.clear();
int n; read(n);
for(int i = 1; i <= n; i++){
int x; read(x);
}
cout << C(2, 10 - n) * 6 << endl;
}
return 0;
}
B. Permutation Value
大水题。首先可以知道小的数字在一个排列中是不可缺少的。所以把小数字尽量放远一点即可。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define N 1000010
#define ll long long
template <class T>
inline void read(T& a){
T x = 0, s = 1;
char c = getchar();
while(!isdigit(c)){ if(c == '-') s = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + (c ^ '0'); c = getchar(); }
a = x * s;
return ;
}
int ans[N];
int main(){
// freopen("hh.txt", "r", stdin);
int T;read(T);
while(T--){
memset(ans, 0, sizeof(ans));
int n; read(n);
int tot = 0;
int p = 1;
int dis = 0;
for(int i = 1; i <= n; i++){
ans[p] = i;
if(i % 2 == 0) p = dis + 1;
else p = n - dis, dis++;
}
for(int i = 1; i <= n; i++){
printf("%d ", ans[i]);
}
cout << endl;
}
return 0;
}
C. Save the Magazines
相对没那么水。设 \(dp\) 数组 \(dp_{i,0/1}\) 表示第 \(i\) 件物品为止,是否转移到前一个物品,情况下的最大值。公式见代码。注意讨论是否可以转移到前一个物品以及前提条件。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define N 1000010
#define ll long long
template <class T>
inline void read(T& a){
T x = 0, s = 1;
char c = getchar();
while(!isdigit(c)){ if(c == '-') s = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + (c ^ '0'); c = getchar(); }
a = x * s;
return ;
}
int n;
char s[N];
int a[N];
int dp[N][2];
int main(){
// freopen("hh.txt", "r", stdin);
int T; read(T);
while(T--){
// memset(dp, 0, sizeof(dp));
read(n);
scanf("%s", s + 1);
for(int i = 1; i <= n; i++) read(a[i]);
for(int i = 1; i <= n; i++){
if(s[i] == '1'){
if(s[i-1] == '1') dp[i][1] = dp[i-1][1] + a[i-1];
else dp[i][1] = dp[i-1][0] + a[i-1];
dp[i][0] = max(dp[i-1][0], dp[i-1][1]) + a[i];
}
else{
dp[i][1] = -1e9;
dp[i][0] = max(dp[i-1][0], dp[i-1][1]);
}
}
cout << max(dp[n][0], dp[n][1]) << endl;
}
return 0;
}
D. Problem with Random Tests
本次比赛争议很大的一道题。由于全是随机数据,难度大大降低。首先容易想到,
可以贪心找到以 \(1\) 开头的一个长串,这样肯定可以保证结果尽量大。然后考虑寻找第二个串来跟他 \(or\) 一下。由于纯随机数据,复杂度期望 \(O(logN)\),很难卡掉,直接一个个暴力比较即可。
(这道题使用 string 会比较好写。本人比赛时太傻了,使用了数组。。。。)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define N 1000010
#define ll long long
template <class T>
inline void read(T& a){
T x = 0, s = 1;
char c = getchar();
while(!isdigit(c)){ if(c == '-') s = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + (c ^ '0'); c = getchar(); }
a = x * s;
return ;
}
int n;
char t[N];
int s[N];
int tmp[N];
int ans[N];
int m;
bool check(int *a, int *b, int n){ // 检查 a 是否大于等于 b
for(int i = 1; i <= n; i++){
if(a[i] < b[i]) return 0;
if(a[i] > b[i]) return 1;
if(a[i] == b[i]) continue;
}
return 1;
}
int main(){
// freopen("hh.txt", "r", stdin);
read(m);
scanf("%s", t + 1);
int p = -1;
for(int i = 1; i <= m; i++)
if(t[i] == '1'){
p = i;
break;
}
if(p == -1){
cout << 0 << endl;
return 0;
}
n = m - p + 1; // 当前串的总长度
int id = 0;
for(int i = p; i <= m; i++){
s[++id] = t[i] - '0'; // 第一个串
}
for(int i = 1; i <= n; i++){
if(s[i] == 0){
p = i;
break;
}
}
memcpy(ans, s, sizeof(ans));
int len = n - p + 1; // 小串的长度
for(int i = 1; i + len <= n; i++){ // i: 小船的头
memcpy(tmp, s, sizeof(tmp)); // 存一个副本
if(s[i] != 1) continue;
for(int j = p, now = 0; j <= n && now < len; j++, now++){
tmp[j] |= s[i+now];
}
if(check(tmp, ans, n)) memcpy(ans, tmp, sizeof(ans));
}
for(int i = 1; i <= n; i++)
printf("%d", ans[i]);
//未完待续
return 0;
}
E. FTL
本蒟蒻到 \(E\) 题便有些吃力了。考虑 \(dp\)。设 \(dp_x\) 表示造成 \(x\) 伤害所需要的最小时间。 考虑到若将剩余血量设为状态,可能出现负数,造成下标问题,故将其反向。
状态转移: 对于当前血量 \(x\),我们有三种情况:用一号大炮单独打一次,用二号大炮单独打一次,二者一起打一次(中间可能包扩各自打)。对于前两种情况,比较简单:
\(dp[x] = min(dp[x - (p1 - p)] + t1, dp[x - (p2 - p)] + t2\)
对于第三种情况,画图考虑:
对于 \(t_2\) 大于 \(t_1\) 的情况,如此考虑:两门炮先各自打若干炮,然后最后一起打一炮。这样就可以覆盖所有的情况了。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define N 1000010
#define ll long long
template <class T>
inline void read(T& a){
T x = 0, s = 1;
char c = getchar();
while(!isdigit(c)){ if(c == '-') s = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + (c ^ '0'); c = getchar(); }
a = x * s;
return ;
}
ll dp[N];
ll p1, t1;
ll p2, t2;
ll M, p;
int main(){
// freopen("hh.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
read(p1), read(t1);
read(p2), read(t2);
read(M), read(p);
memset(dp, 0x3f3f3f3f3f3f3f, sizeof(dp));
dp[0] = 0;
for(int i = 1; i <= M; i++){
dp[i] = min(dp[i], dp[max(0ll, i - p1 + p)] + t1);
dp[i] = min(dp[i], dp[max(0ll, i - p2 + p)] + t2);
for(int j = 1; j <= i; j++){
if(j * t1 >= t2){
ll tmp = (j - 1) * (p1 - p) + ((j * t1 - t2) / t2) * (p2 - p) + (p1 + p2 - p);
dp[i] = min(dp[i], dp[max(0ll, (ll)i-tmp)] + j * t1);
}
if(j * t2 >= t1){
ll tmp = (j - 1) * (p2 - p) + ((j * t2 - t1) / t1) * (p1 - p) + (p1 + p2 - p);
dp[i] = min(dp[i], dp[max(0ll, (ll)i-tmp)] + j * t2);
}
}
}
cout << dp[M] << endl;
return 0;
}