https://ac.nowcoder.com/acm/contest/11746
好久没写过代码了,思维能力下降,爬了
A. 切蛋糕
待补
B. 小宝的幸运数组
套路题,初始化tmp为0,从头遍历一遍数组,用tmp加上当前a[i]再对k取模,如果有两个位置得到的模数一样,说明这一段的和就是k的倍数。因为要求最长的子列,注意到k的范围只有1e5,因此只需要用一个桶记录某个得到的模数最开始出现的位置即可。
#include <iostream>
#define int long long
using namespace std;
int n, k, a[200005];
int pre[200005] = {-1};
signed main()
{
int t;
cin >> t;
while(t--)
{
cin >> n >> k;
long long tot = 0;
for (int i = 0; i <= 100005; i++)
pre[i] = -1;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
tot += a[i];
}
if(n == 1)
{
if(a[1] == k) cout << 1 << endl;
else cout << -1 << endl;
continue;
}
if(k == 1|| tot % k == 0)
{
cout << n << endl;
continue;
}
int tmp = 0;
int ans = -1;
a[0] = 0;
for (int i = 0; i <= n; i++)
{
tmp = (tmp + a[i]) % k;
//cout << tmp << ' ';
if (pre[tmp] == -1)
pre[tmp] = i;
else
{
//cout << tmp << ' ' << pre[tmp] << endl;
ans = max(ans, i - pre[tmp]);
}
}
//cout << endl;
cout << ans << endl;
}
return 0;
}
C. 上进的凡凡
dp水题。dp[i]代表以a[i]结尾的非降连续子数组的个数,有转移方程: dp[i] += (a[i] >= a[i - 1]) ? dp[i - 1] : 0;
统计答案即可。
#include <iostream>
using namespace std;
long long n, a[100005], dp[100005];
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
dp[i] = 1;
}
long long ans = 0;
for (int i = 1; i <= n; i++)
{
if(a[i] >= a[i - 1])
dp[i] += dp[i - 1];
ans += dp[i];
}
cout << ans;
return 0;
}
D. Seek the Joker I
巴什博弈(比赛的时候脑补的)。先手最优策略是等后手拿x张以后选择拿k + 1 - x张,这样相当于拿了有限组的k + 1张,用n % (k + 1) 得到剩下的,这时候先手只要在第一次拿的时候拿走n % (k + 1) - 1张,这样最终会留下一张小丑给后手。如果没法这样拿的话说明先手必败。
#include <iostream>
using namespace std;
int main()
{
int t;
cin >> t;
while(t--)
{
int n, k;
cin >> n >> k;
int tmp = n % (k + 1);
if(tmp > 1 || tmp == 0)
cout << "yo xi no forever!" << endl;
else
cout << "ma la se mi no.1!" << endl;
}
return 0;
}
E. Seek the Joker II
威佐夫博弈,不会QAQ
F. 成绩查询ing
据说可以map瞎搞
G. 贪吃的派蒙
H. 数羊
估计是改编自HDU1165。直接暴力不可取,但注意到m范围只有0,1,2,因此考虑求出来一部分公示缩减递归的规模,比如可以求出来m==1的情况,剩下的再递归就可以了。答案把所有公式都写出来了也可以。
#include <iostream>
#define p 998244353
using namespace std;
long long n, m;
long long A(long long n, long long m)
{
if(n == 1 && m == 0)
return 2;
if(n == 0)
return 1;
if(m == 0)
return (n % p + 2) % p;
if(n == 1 && m == 1)
return 2;
if(m == 1)
return 2 * n;
return A(A(n - 1, m), m - 1) % p;
}
int main()
{
int t;
cin >> t;
while(t--)
{
cin >> n >> m;
cout << A(n, m) % p << endl;
}
return 0;
}
I. 买花
可以随便瞎搞,我选择求一个前缀和然后枚举子数组判断和能否整除n。
#include <iostream>
#define int long long
using namespace std;
int a[105], sum[105];
signed main()
{
//freopen("data.txt", "r", stdin);
a[1] = 1;
sum[1] = 1;
for(int i = 2; i <= 20; i++)
{
a[i] = a[i - 1] * 2;
sum[i] = sum[i - 1] + a[i];
}
int t;
cin >> t;
while(t--)
{
int n;
cin >> n;
bool flag = 0;
for(int i = 1; i <= 15; i++)
{
for(int j = i + 1; j <= 15; j++)
{
int s = sum[j] - sum[i - 1];
if(n % s == 0)
{
flag = 1;
break;
}
}
}
if(flag) cout << "YE5" << endl;
else cout << "N0" << endl;
}
return 0;
}
J. 这是一道简单的模拟
题目说的没错。开二维数组写即可。
#include <iostream>
#include <vector>
#define N 305
#define M 10005
using namespace std;
int n, m, k;
// k, head[N], ver[2 * M], edge[2 * M], Next[2 * M], tot = 0;
// void add(int x, int y, int z)
// {
// ver[++tot] = y, edge[tot] = z, Next[tot] = head[x], head[x] = tot;
// }
int mmap[305][305] = {0};
int main()
{
cin >> n >> m;
for (int i = 1; i <= m; i++)
{
int x, y, z;
cin >> x >> y >> z;
//add(x, y, z);
//add(y, x, z);
mmap[x][y] = mmap[y][x] = z;
}
cin >> k;
long long cost = 1e15;
for (int i = 1; i <= k; i++)
{
int num;
cin >> num;
vector<int> v;
v.push_back(0);
long long cc = 0;
bool flag = 1, vis[305] = { 0 };
for (int i = 1; i <= num; i++)
{
int p;
cin >> p;
v.push_back(p);
}
v.push_back(0);
for (int i = 1; i < v.size(); i++)
{
vis[v[i]] = 1;
if (!mmap[v[i]][v[i - 1]])
{
flag = 0;
break;
}
cc += 1ll * mmap[v[i]][v[i - 1]];
}
for (int i = 1; i <= n; i++)
{
if(!vis[i])
{
flag = 0;
break;
}
}
if(flag)
cost = min(cost, cc);
}
if(cost != 1e15) cout << cost;
else
cout << -1;
return 0;
}
K. 黑洞密码
模拟即可,注意循环后移和平时不同,由于最多往后移动九次,因此按照下述代码搞蛮方便的。
#include <iostream>
#include <vector>
using namespace std;
string s1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZbcdefghijklmn";
string s2 = "abcdefghijklmnopqrstuvwxyzBCDEFGHIJKLMN";
char v1[400];
int v2[400];
char ans[400];
int main()
{
//freopen("data.txt", "r", stdin);
string s;
cin >> s;
int pos1 = 0, pos2 = 0;
for (int i = 0; i < s.size(); i++)
{
if(s[i] >= '0' && s[i] <= '9')
v2[pos2++] = s[i] - '0';
else
v1[pos1++] = s[i];
}
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
char tmp = v1[i * 4 + j];
if (tmp >= 'a' && tmp <= 'z')
{
ans[i * 4 + j] = s2[(int)(tmp - 'a') + v2[i * 4 + j]];
}
else
{
ans[i * 4 + j] = s1[(int)(tmp - 'A') + v2[i * 4 + j]];
}
}
}
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 2; j++)
{
swap(ans[i * 4 + j], ans[i * 4 + 3 - j]);
}
}
for(int i = 0; i < 16; i++) cout << ans[i];
}
L. 建立火车站
看到最大距离最小想到二分,直接对答案区间进行二分,check的时候判断每一段区间能否用现有的k个停靠站分成最大不超过mid的子区间,能的话更新k同时判断下一个区间,不能的话return false。
#include <iostream>
#include <vector>
#include <algorithm>
#define int long long
using namespace std;
long long n, k, pos[100005];
vector<long long> v;
bool check(long long mid)
{
long long kk = k;
for (int i = 0; i < v.size(); i++)
{
int num = v[i] / mid;
if(v[i] % mid != 0)
num++;
num--;
if(kk < num)
{
return false;
}
kk -= num;
}
return true;
}
signed main()
{
cin >> n >> k;
for (int i = 1; i <= n; i++)
cin >> pos[i];
sort(pos + 1, pos + n + 1);
for (int i = 2; i <= n; i++)
v.push_back(pos[i] - pos[i - 1]);
sort(v.begin(), v.end());
long long l = 1, r = v[v.size() - 1], mid;
while(l < r)
{
mid = (l + r) >> 1;
//cout << mid << endl;
if (check(mid))
r = mid;
else
l = mid + 1;
}
cout << l;
return 0;
}