B. Napoleon Cake
因为每个层对应的 a[i] 都是向下渗透,所以我们选择从后往前扫一遍。
开始设置一个cnt表示当前还可以渗透的多少层。
每次维护一下最大的
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e6 + 5;
ll a[N], ans[N];
ll n, m, T, x;
signed main()
{
cin >> T;
while(T--)
{
cin >> n;
for(int i = 1;i <= n;i++)
{
scanf("%lld", &a[i]);
ans[i] = 0;
}
ll cnt = 0;
for(int i = n; i >= 1; --i)
{
cnt = max(cnt, a[i]);
ans[i] = (cnt > 0);
--cnt;
}
for(int i = 1; i <= n; ++i)
printf("%lld%c", ans[i] > 0 ? 1ll : 0ll, i == n ? '\n' : ' ');
}
return 0;
}
B. AGAGA XOOORRR
位运算:异或
题意:选两个相邻的数,用异或值取代两个数,数组长度 -1,要求最后剩下的数都相等,至少两个。
分析:最后剩下 k 个相等的数, sum为所有数异或值
-
sum = 0, 一定留下 k 个相等的数,k 为偶数
-
sum != 0 ,如果存在,那么 k 一定为奇数
-
被 sum 分割的区间异或值为0,如果区间个数 >= 3, 就满足条件
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2005;
int a[N];
int t, n;
int main()
{
cin >> t;
while (t--)
{
cin >> n;
int sum = 0;
for (int i = 1; i <= n; i++) scanf("%d", &a[i]), sum ^= a[i];
if (sum == 0)
{
printf("YES\n");
continue;
}
int cnt = 0;
for (int i = 1, j; i <= n; i = j + 1)
{
j = i;
int now = a[i];
while (j < n && now != sum) ++j, now ^= a[j];
if (now == sum) ++cnt;
}
if (cnt >= 3) printf("YES\n");
else printf("NO\n");
}
return 0;
}
B. Binary Removals
因为我们要在序列前边删除多余的1,如果出现 11,我们就试图删除在此后边多余的并且不连续的0,如果在出现 11 之后 出现了 00,那么就是NO,否则就是 YES
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2005;
int a[N];
int t, n;
string s;
int f[N];
int main()
{
cin >> t;
while (t--)
{
cin >> s;
bool f1 = 0, f0 = 0, f = 1;
for(int i = 1; i < s.length(); ++i)
{
if(s[i] == s[i-1] && s[i] == '1') f1 = 1;
else if(s[i] == s[i-1] && s[i] == '0' && f1) f0 = 1;
if(f1 && f0)
{
f = 0;
break;
}
}
if(f) cout << "YES\n";
else cout << "NO\n";
}
return 0;
}
B. Replace and Keep Sorted
注意 所找的数组只能有一个数和原数组不同
每次只需要在所选的数组中 替换一个数即可
对于每个数求出可替换的数的数目
由于查询次数比较多,所有想到了前缀和。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 9;
int a[N];
int t, n, q, k;
int l, r;
string s;
int f[N];
int main()
{
cin >> n >> q >> k;
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
if(n == 1)
{
while(q--)
{
cout << k-1 << endl;
}
return 0;
}
for(int i = 1; i <= n; ++i)
{
if(i == 1)
f[1] = a[2] - 1 - 1;
else if(i == n)
f[n] = k - a[n-1] - 1;
else f[i] = a[i+1] - a[i-1] - 2;
f[i] += f[i-1];
}
for(int i = 1; i <= n; ++i)
cout << f[i] << " ";cout << endl;
while(q--)
{
scanf("%d %d", &l, &r);
if(l == r)
{
cout << k - 1 << endl;continue;
}
int ans = f[r-1] - f[l] + a[l+1] - 1 - 1 + k - a[r-1] - 1;
// 替换从 a[l+1] --->a[r-1] 替换 a[l] 替换 a[r]
cout << ans << endl;
}
return 0;
}
C. Maximum width
题意:就是在 s 串找出 t 串(保证一定有),然后找出求 所有找出的 t 串中,两个字符下标差值最大值
贪心做的,从左向右扫,存每个字符下标的最左端,然后从右向左扫,存每个字符下标的最右端,然后求最大的一个下标差即可。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 9;
int n, m;
string s, t;
vector <int> v1, v2;
int Max;
int main()
{
cin >> n >> m;
cin >> s >> t;
for(int i = 0, j = 0; i < n && j < m; ++i)
{
if(s[i] == t[j]) v1.push_back(i), ++j;
}
for(int i = n-1, j=m-1; i >= 0 && j >= 0; --i)
{
if(s[i] == t[j]) v2.push_back(i), --j;
}
reverse(v2.begin(),v2.end());
for(int i = 1; i < v1.size(); ++i)
{
Max = max(Max,v2[i] - v1[i-1]);
//cout << v1[i] << " " << v2[i] << endl;
}
cout << Max << endl;
return 0;
}
C. Long Jumps
数据比较大, 如果我们暴力肯定会超时
考虑到每次我们都是从前往后扫,并且结束都是下标大于n的时候结束
因为当前的下标是小于n的,它往后的路径是确定的,所以我们从后往前扫,存后边的路径,扫到前边时可以直接返回答案,就是类似于记忆化搜索。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 9;
int t, n;
long long a[N];
int f[N];
int main()
{
cin >> t;
while(t--)
{
memset(f,0,sizeof(f));
cin >> n;
for(int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
long long Max = 0;
for(long long i = n; i >= 1; --i)
{
// 当前为 i
long long k = i + a[i];// 下一个
while(k <= n)
{
if(f[k])// 类似于记忆化搜索
{
k += f[k];break;
}
k += a[k];
}
Max = max(k-i, Max);
f[i] = k - i;
}
cout << Max << endl;
}
return 0;
}
D. Even-Odd Game
贪心
每次选取当前最大的数,要么加上给自己,要么移除也不给对方加分。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 9;
int t, n;
long long a[N];
bool cmp(long long x, long long y)
{
return x > y;
}
//int f[N];
int main()
{
cin >> t;
while(t--)
{
cin >> n;
for(int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
sort(a+1,a+1+n,cmp);
long long sum1 = 0, sum2 = 0;
for(long long i = 1; i <= n; ++i)
{
if(i & 1 && a[i] % 2 == 0) sum1 += a[i];
if(i % 2 == 0 && a[i] & 1) sum2 += a[i];
}
if(sum1 > sum2) cout << "Alice\n";
else if(sum1 < sum2) cout << "Bob\n";
else cout << "Tie\n";
}
return 0;
}
题意:给你一个数组和x,你的初始分数为0。从头开始遍历数组,对于当前的元素,不能整除x时就结束,输出分数;可以整除x就将分数增加当前数字大小,并且在数组末尾添加x个大小为a[i]除以x的元素。
如果按照题意来模拟数组肯定会爆掉…其实我们不需要不断更新数组a,a[i] = a[i] / x,但只要a[i] % x == 0 那么就可以贡献初始的a[i]一次。
这里要注意初始的分数其实是给你的数组元素和,b[i]记录初始a[i]
由于我们会不断向数组里增添数据,终止的条件是恰好遇到数组中某个不能整除x的数,所以不能直接对一个数据进行分解。
#include <bits/stdc++.h>
using namespace std;
#define qc std::ios::sync_with_stdio(0);
int a[100001];
int b[100001];
int main() {
qc;
cin.tie(0);
int t;
cin >> t;
while (t--) {
int n, k;
long long ans = 0;
cin >> n >> k;
for (int i = 0; i < n; i++) {
cin >> a[i];
b[i] = a[i];
ans += a[i];
}
int flag = 0;
while (1) {
for (int i = 0; i < n; i++) {
if (a[i] % k != 0) {
flag = 1;
break;
} else {
a[i] = a[i] / k;
ans += b[i];
}
}
if (flag == 1)
break;
}
cout << ans << endl;
}
}
C1. Pokémon Army (easy version)
wa了一个小时,没改出来
网上一搜这个代码居然a了,我直接震惊,擦
这可能就是贪心解法?
#include<bits/stdc++.h>
#define ll long long
#define ios ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=3e5+5,INF=0x3f3f3f3f;
ll a[maxn];
int main()
{
ios;
int t;
cin >> t;
while(t--)
{
int n, q;
cin >> n >> q;
for(int i = 1; i <= n; i++)
cin >> a[i];
a[n + 1] = 0;
ll ans = 0;
for(int i = 1; i <= n; ++i)
{
if(a[i + 1] < a[i])
ans += a[i] - a[i + 1];
}
cout << ans << endl;
}
return 0;
}
dp解法
粘一下博客叭
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e5 + 9;
ll dp[N][2];
ll a[N];
int main()
{
int t, n, q;
cin >> t;
while(t--)
{
memset(dp,0,sizeof(dp));
cin >> n >> q;
a[n+1] = 0;
for(int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
for(int i = 1; i <= n; ++i)
{
dp[i][0] = max(dp[i-1][0], dp[i-1][1] - a[i]);
dp[i][1] = max(dp[i-1][1], dp[i-1][0] + a[i]);
}
cout << max(dp[n][0], dp[n][1]) << endl;
}
return 0;
}
B. Nastia and a Good Array
类似构造通解的题
#include<bits/stdc++.h>
using namespace std;
const int maxn=100000+50;
int n,m,ans,T,a[maxn];
int main()
{
scanf("%d",&T);
for (int oo=1; oo<=T; ++oo)
{
scanf("%d",&n);
for (int i=1; i<=n; ++i) scanf("%d",&a[i]);
printf("%d\n",n/2);
for (int i=1; i+1<=n; i+=2)
{
int t=min(a[i],a[i+1]);
if (t>=998244353)
{
if (t==a[i-1]) ++t;
printf("%d %d %d %d\n",i,i+1,t,t+1); a[i+1]=t+1;
}
else
{
printf("%d %d %d %d\n",i,i+1,t,998244353);
a[i+1]=998244353;
}
}
}
return 0;
}
A. Omkar and Bad Story
假如只有正数,那么每次相减取绝对值只会得到一个不大于这两个数较大的数的数,如果有负数,那么它和任意一个数作差都会让需要添加的数越来越大,不可能存在每个数都在数组里。
#include<bits/stdc++.h>
using namespace std;
int t, n;
int a;
void solve()
{
bool f = 1;
cin >> n;
for(int i = 1; i <= n; ++i) {
cin >> a;if(a < 0) f = 0;
}
if(!f) cout << "NO\n";
else
{
cout << "YES\n";
cout << "101\n";
for(int i = 0 ; i <= 100; ++i) cout << i <<" ";cout << endl;
}
}
int main()
{
cin >> t;
while(t--)
solve();
return 0;
}
B. Pleasant Pairs
解法一:用结构体存一下所有元素的值和下标,然后sort一下值,从小到大
两层for循环遍历,元素乘积 > 2*n 就break掉。
(这个思路比较简单,好理解
解法二:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 2e5 + 9;
ll t = 1, n, m, k;
ll pos[N];
int main()
{
int t;
cin >> t;
while(t--)
{
scanf("%lld", &n);
for(int i = 1; i <= 2*n; ++i) pos[i] = 0;
for(ll i = 1, c; i <= n; ++i) scanf("%lld", &c), pos[c] = i;
ll ans = 0;
for(int i = 3; i <= 2*n-1; ++i)// 遍历 i+j的和
{
for(int j = 1; 1ll*j*j < i; ++j)// 遍历加和的因子
{
if(i % j != 0) continue;// i % j 必须等于0
int k = i / j;// 也就是 k 必须为整数,因为它是因子
if(pos[j] && pos[k] && pos[j] + pos[k] == i) ++ans;
// 这两个因子存在,并且下标加和等于 i 就++ans
// 查询这两个因子中有没有输入的时间复杂度仅为 O(1)
}
}
cout << ans << endl;
}
return 0;
}
C. Great Graphs
看题目意思好像是,把1-n走一遍,加上1-n的所有正边权(排序完之后的第n个边),然后减去任意两边之间的边权值,就是答案
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
const int N = 1e5 + 9;
typedef long long ll;
ll t = 1, n, m, k;
ll a[N];
int main()
{
cin >> t;
while(t--)
{
cin >> n;
ll sum = 0;
for(int i = 1; i <= n; ++i) scanf("%lld", &a[i]), sum += a[i];
sort(a+1,a+1+n);
ll ans = a[n];
ans -= sum;// 先减去所有边到 1 的边权值
for(int i = 1; i < n; ++i)
{
sum -= (a[i+1] - a[i]) * (n - i);// sum里面 i ---> i + 1 的边加了 n - i 次
ans -= sum;//然后减去所有边到 i+1 的边权值
}
cout << ans << endl;
}
return 0;
}