文章目录
感想
不会写,写的好墨迹
A. Forbidden Integer
1.题目内容
2.个人思路
如果1可用就全堆1,否则如果是偶数就全堆2,是奇数就全堆2加一个3
判断一下k能不能取到2和3就可以。
3.代码
int main() {
cin >> t;
while (t--) {
ll k, x;
cin >> n >> k >> x;
vector<ll> ans;
if (x != 1) {
cout << "YES\n";
cout << n << "\n";
for (int i = 1; i <= n; i++) {
cout << "1 ";
}
cout << "\n";
}
else {
if (n % 2 == 0) {
if (k < 2) {
cout << "NO\n";
}
else {
cout << "YES\n";
cout << n / 2 << "\n";
for (int i = 1; i <= n / 2; i++) {
cout << "2 ";
}
cout << "\n";
}
}
else {
if (k < 3) {
cout << "NO\n";
}
else {
cout << "YES\n";
cout << n / 2 << "\n";
for (int i = 1; i <= n / 2 - 1; i++) {
cout << "2 ";
}
cout << "3\n";
}
}
}
}
return 0;
}
B. Come Together
1.题目内容
2.个人思路
以A为中心建立坐标轴,判断一下两个人的家的象限,如果相同就是两个坐标分别取最小相加再加1,如果分别位于1 4或2 3就是最小的x轴 + 1
如果分别位于1 2或3 4就是最小的y轴 + 1,如果分别位于1 3或2 4就是1.
证明的话画一下就能看出来。
3.代码
int main() {
cin >> t;
while (t--) {
ll x, y;
cin >> x >> y;
ll a, b, c, d;
cin >> a >> b;
cin >> c >> d;
ll bx = (x - a), by = (y - b);
ll cx = (x - c), cy = (y - d);
if (bx * cx <= 0 && by * cy <= 0) {
cout << "1\n";
}
else if (bx * cx <= 0 && by * cy > 0) {
cout << min(abs(by), abs(cy)) + 1 << "\n";
}
else if (bx * cx > 0 && by * cy <= 0) {
cout << min(abs(bx), abs(cx)) + 1 << "\n";
}
else if (bx * cx > 0 && by * cy > 0) {
cout << min(abs(bx), abs(cx)) + min(abs(cy), abs(by)) + 1 << "\n";
}
}
return 0;
}
C. Strong Password
1.题目内容
2.个人思路
贪心,显然前面的密码选取的第一次出现位置越靠后,剩下的数据库长度就越短,能碰到相应数字的概率就越低,所以每次都选择当前在数据库种最靠后的位置的数字,然后判断是否每个都能取到就可以。
3.代码
ll t, n, m, p = 998244353;
ll arr[200005], sum[300005][10];
set<ll> pos[10];
int main() {
cin >> t;
while (t--) {
string s;
cin >> s;
n = s.size();
cin >> m;
string l, r;
cin >> l >> r;
for (int i = 0; i < 10; i++) {
pos[i].clear();
sum[0][i] = 0;
}
for (int i = 0; i < n; i++) {
ll tmp = s[i] - '0';
pos[tmp].insert(i);
}
bool ans = false;
ll ma = -1, mp = -1, now = -1;
for (int i = 0; i < m; i++) {
for (int j = l[i] - '0'; j <= r[i] - '0'; j++) {
auto x = pos[j].upper_bound(now);
if (x == pos[j].end()) {
ans = true;
break;
}
if (*x > ma) {
ma = *x;
mp = j;
}
}
if (ans) break;
now = ma;
}
if (ans) {
cout << "YES\n";
}
else {
cout << "NO\n";
}
}
return 0;
}
D - Rating System
1.题目内容
2.个人思路
dp转移,k的值为前i个数字的前缀和,最大值的对应k集合中一定至少会有一个1~x的前缀和。
问题转化成在1~x的前缀和下的最优结果,考虑dp。
dp[i]为以1~i - 1的前缀和为k(i == 1 时为0)时,i到n的变化让rating带来的提升。(为什么不是真正的提升,因为假设1 ~ i - 1的前缀和为x,在i - 1之前可能已经到达或者超过过x,那么这个结果就不好算了,而且实际上最优情况下1 ~ i - 1不应该存在超过前缀和的点,所以说dp的转移是正确的。)
从后往前dp,如果arr[i]大于0,那么这个点不应该用来转移,因为一定会不是最优点,如果小于0,有两种情况:
后面的前缀和不会低于sum[i]:dp[i]就是arr[i]的绝对值。
dp[i] = abs(arr[i])
存在后面的前缀和低于sum[i]:假设后面最近的低于i的位置low,那么:
dp[i] = dp[low] - (sum[low - 1] - sum[i - 1])
可以理解为,因为sum[low]前缀和小于low,所以到 low 之后的变化应该和dp[low]接近,但是带来的增益会因为sum的变化而变化。
可以考虑 -1 2 -3这个例子,在i = 3时,他是以前缀和为1([1, 2])进行增减,而在i = 1时,因为它前面是0,所以转移到3的时候需要把[1, 2]减掉才是3在i = 1的情况下实际变化到的数字(也就是sum[0] = 0)。
单调栈处理出第一个小于当前前缀和的数字,然后dp处理,最后比较最大输出结果即可。
其实我也不知道我说的对不对,希望有人指点下
3.代码
ll t, n, m, p = 998244353;
ll arr[300005], sum[300005];
ll dp[300005], low[300005];
int main() {
cin >> t;
while (t--) {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> arr[i];
low[i] = 0;
sum[i] = sum[i - 1] + arr[i];
}
ll k = 0, nn = -1;
stack<ll> sto;
for (int i = 1; i <= n; i++) {
while (!sto.empty() && sum[sto.top()] > sum[i]) {
low[sto.top()] = i;
sto.pop();
}
sto.push(i);
}
for (int i = n; i > 0; i--) {
if (arr[i] > 0) {
dp[i] = -1;
}
else {
if (low[i]) {
dp[i] = dp[low[i]] - sum[low[i] - 1] + sum[i - 1];
}
else dp[i] = abs(arr[i]);
}
}
for (int i = 1; i <= n; i++) {
if (nn < dp[i]) {
nn = dp[i];
k = sum[i - 1];
}
}
cout << k << "\n";
}
return 0;
}
其他想法
D的转移并非当前状态的真正最优解,只是使用局部最优来处理,我也不知道对不对,急了就开始魔怔了。