AcWing 每日一题 - Summer
本篇解题记录题源来自 AcWing 的 Summer 每日一题
补题链接:Here
2021/07/01 done
Week 1
星期一 AcWing 3485. 最大异或和 (Hard
思路
先求出前i个数的异或和sum[i],再在大小为m的滑动窗口内进行trie.
- \(\mathcal{O}(nlog\ n)\)
int n, m;
const int N = 31e5 + 10; //最多有n*31
int p[N][35], ct[N], idx; //ct[n]的作用是标记滑动窗口内0,1的数量
int sum[100010]; //sum[i]存前i个数的异或和
void insertt(int u, int c) {
int t = 0;
for (int i = 30; i >= 0; i--) {
int x = u >> i & 1;
if (!p[t][x]) {
p[t][x] = ++idx;
}
t = p[t][x];
ct[t] += c; //标记这里(有或删除)一个数可以达到该位置
}
}
int query(int u) {
int t = 0;
int res = u;
for (int i = 30; i >= 0; i--) {
int x = u >> i & 1;
if (ct[p[t][!x]] > 0) { //当x对面的那个数!x存在时(0,1)
x = (x + 1) % 2; //x就变成另外一个数 !x
}
res ^= x << i;
t = p[t][x];
}
return res;
}
void solve() {
cin >> n >> m;
int t;
for (int i = 1; i <= n; i++) {
cin >> t;
sum[i] = sum[i - 1] ^ t; //sum[i]表示前i个数的^
}
insertt(0, 1); //插入0,是为了方便前m个数进行异或得出的答案可以是它本身的值
int res = 0; //求最大值
for (int i = 1; i <= n; i++) {
if (i > m) insertt(sum[i - m - 1], -1); //将滑动窗口外的数除去,这时就要修改ct,故-1
res = max(res, query(sum[i])); //在滑动窗口内求最大值
insertt(sum[i], 1); //求完后记得插入该值,方便后面的值进行异或
}
cout << res;
}
星期二 AcWing 3493. 最大的和 (Easy
对于 \(30\%\) 数据,可以开两重 for 循环暴力找
但在 \(100\%\) 数据里肯定会 T
所以要用前缀和进行优化 (当然也可以用队列优化,这里就不展开了,贴一个讲解链接
- \(\mathcal{O}(n)\)
using ll = long long;
const int N = 1e5 + 10;
ll a[N], s[N];
ll st[N];
void solve() {
int n, k;
cin >> n >> k;
for (int i = 1; i <= n; ++i) cin >> a[i];
ll sum = 0;
for (int i = 1; i <= n; ++i) {
cin >> st[i];
if (st[i]) s[i] += s[i - 1], sum += a[i]; // 如果是 st[i] = 1 的情况说明直接就可选,sum 累加即可
else
s[i] = s[i - 1] + a[i]; // 需要转换状态的话则要利用前缀和累加了
}
ll res = 0;
for (int i = k; i <= n; ++i) res = max(res, s[i] - s[i - k]);
cout << res + sum;
}
星期三 AcWing 3499. 序列最大收益 (Mid
参考最长上升子序列
f[i][j]
表示从0~i-1
中删j
个点后,0~i
的收益最大值- 或者说成前i个点中删j个数,保留i的最大收益
- 按i来划分集合,可以划分为0~i-1,其中0就代表前0个点删j个数,等价于不删.
- f[i][j]可以从f[u][?]转移过来,我们对f[i][j]做一个转换
- 既然要删j个数,可以先把后面[u+1,i-1]给删了,这样会删除i-1-u个数
- 然后再在前面[0,u]中删j-(i-1-u)个数,此时u点和i点临近,可加贡献
w[a[u]][a[j]]
故可以得到如下状态转移方程f[i][j] = max(f[i][j],f[u][j-(i-u-1)]+w[a[u]][a[i]]);
const int N = 210;
int n, k, m;
int a[N]; // 记录元素
int w[N][N]; // w[i][j]表示i->j的收益
int f[N][N]; // f[i][j] 表示0~i-1中删除j个点后,0~i的最大收益
void solve() {
cin >> n >> k >> m;
for (int i = 1; i <= m; ++i) cin >> a[i];
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
cin >> w[i][j];
memset (f, -0x3f, sizeof (f) );
f[1][0] = 0; // init
// 第1个点删0个数保留1的收益为0
for (int i = 1; i <= m; i ++ )
for (int j = 0; j <= k; j ++ ) //j从0~k,不删就是0
for (int u = 1; u < i; ++ u) //最多删i-1个
if (j >= i - u - 1) //注意是>=,=的条件下就说明既可以把后面删完,也可以直接分配到0~i中
//故要计算状态
f[i][j] = max (f[i][j], f[u][j - (i - u - 1)] + w[a[u]][a[i]]);
int ans = 0;
for (int i = 0; i <= k; ++i) ans = max (ans, f[m][i]);
cout << ans << '\n';
}
星期四 AcWing 3502. 不同路径数
这道题,爆搜 + set去重即可
const int N = 10;
int n, m, k;
int e[N][N];
set<int>S;
int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0};
int res = 0;
void dfs(int sx, int sy, int u, int s) {
if (u == k) {
if (!S.count(s)) ++res, S.insert(s);
return;
}
for (int i = 0; i < 4; ++i) {
int x = sx + dx[i];
int y = sy + dy[i];
if (x < 1 || x > n || y < 1 || y > m)continue;
dfs(x, y, u + 1, s * 10 + e[x][y]);
}
}
void solve() {
cin >> n >> m >> k;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
cin >> e[i][j];
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) {
dfs(i, j, 0, e[i][j]);
}
cout << res;
}
星期五 AcWing 3510. 最长公共子序列 (Mid
const int N = 1e6 + 7;
int id[N], q[N];
void solve() {
int n;
cin >> n;
memset(id, -1, sizeof(id));
for (int i = 0, x; i < n; ++i) {
cin >> x;
id[x] = i;
}
int tt = 0;
q[0] = -1;
for (int i = 0, x; i < n; ++i) {
cin >> x;
int k = id[x];
if (k == -1)continue;
int l = 0, r = tt;
while (l < r) {
int mid = l + (r + 1) >> 1;
if (q[mid] < k)l = mid;
else r = mid - 1;
}
q[r + 1] = k;
tt = max(tt, r + 1);
}
cout << tt << '\n';
}
星期六 AcWing 3489. 星期几 (Easy)
套公式,或者逐年累加
int Day(int y, int m, int d) {
if (m == 1 || m == 2) m += 12, y -= 1;
return (d + 2 * m + 3 * (m + 1) / 5 + y + y / 4 - y / 100 + y / 400 +
1) % 7;
}
string D[] = {
"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
};
string DD[] = {
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
};
void solve() {
int y, d, mm;
string m;
while ( cin >> d >> m >> y) {
for (int i = 0; i < 12; ++i)
if (m == D[i])cout << DD[(Day(y, i + 1, d))] << "\n";
}
}
星期日 AcWing 3481. 阶乘的和
using ll = long long;
int fact[20];//10!就大于1e6
int combin[4100];//存所有阶乘能组合出来的数,0~10有11种阶乘,2^11空间存储
int m = 0;
void dfs(int u, int st) {
if (u == 11) {
int res = 0;
for (int i = 0; i < 11; ++i) {
if (st >> i & 1) {
res += fact[i];
}
}
combin[m++] = res;
return;
}
dfs(u + 1, st);
dfs(u + 1, st | 1 << u);
}
void solve() {
fact[0] = 1;
for (int i = 1; i <= 10; ++i) fact[i] = fact[i - 1] * i;
dfs(0, 0);
sort(combin, combin + m);
// m = unique(combin, combin + m) - combin; // 去重,可有可无
int x;
while (cin >> x, x >= 0) {
int l = 1, r = m - 1;
int mid = l + r >> 1;
//二分查找预处理数组
while (l < r) {
mid = l + r >> 1;
if (combin[mid] >= x) r = mid;
else l = mid + 1;
}
cout << (combin[l] != x ? "NO\n" : "YES\n");
}
}
Week 2
星期一 AcWing 3516. 最大面积 (Hard
第一眼看过去像二维前缀和问题,但实际要使用单调栈解决
下面给出 4 道相关知识点递增的题目
830.单调栈 -> 131. 直方图中最大的矩形 -> 152. 城市游戏 -> 3516. 最大面积
Y总视频讲解,建议直接空降 30 min
const int N = 2010;
char g[N][N];
int l[N], r[N], q[N];
int U[N], D[N], L[N], R[N];
int s[N][N];
int n, m;
int calc(int h[], int n) {
h[0] = h[n + 1] = -1;
int tt = 0;
q[0] = 0;
for (int i = 1; i <= n; i ++ ) {
while (h[q[tt]] >= h[i]) tt -- ;
l[i] = q[tt];
q[ ++ tt] = i;
}
tt = 0;
q[0] = n + 1;
for (int i = n; i; i -- ) {
while (h[q[tt]] >= h[i]) tt -- ;
r[i] = q[tt];
q[ ++ tt] = i;
}
int res = 0;
for (int i = 1; i <= n; i ++ )
res = max(res, h[i] * (r[i] - l[i] - 1));
return res;
}
void init() { // 注意初始化别写错...
for (int i = 1; i <= n; ++i) { // 枚举行
for (int j = 1; j <= m; ++j)
if (g[i][j] == '1')s[i][j] = s[i - 1][j] + 1;
else s[i][j] = 0;
U[i] = max(U[i - 1], calc(s[i], m));
}
memset(s, 0, sizeof(s));
for (int i = n; i; i--) { // 枚举行
for (int j = 1; j <= m; ++j)
if (g[i][j] == '1')s[i][j] = s[i + 1][j] + 1;
else s[i][j] = 0;
D[i] = max(D[i + 1], calc(s[i], m));
}
memset(s, 0, sizeof(s));
for (int i = 1; i <= m; i ++ ) { // 枚举列
for (int j = 1; j <= n; j ++ )
if (g[j][i] == '1') s[i][j] = s[i - 1][j] + 1;
else s[i][j] = 0;
L[i] = max(L[i - 1], calc(s[i], n));
}
memset(s, 0, sizeof(s));
for (int i = m; i; i -- ) { // 枚举列
for (int j = 1; j <= n; j ++ )
if (g[j][i] == '1') s[i][j] = s[i + 1][j] + 1;
else s[i][j] = 0;
R[i] = max(R[i + 1], calc(s[i], n));
}
}
void solve() {
cin >> n >> m;
for (int i = 1; i <= n; ++i)scanf("%s", g[i] + 1);
init();
int _;
cin >> _;
while (_--) {
int x, y;
cin >> x >> y, x++, y++;
cout << max(max(U[x - 1], D[x + 1]), max(L[y - 1], R[y + 1])) << "\n";
}
}
星期二 AcWing 3404. 谁是你的潜在朋友
按题意来,把看同一本书的人放进数组。
意外的简单
void solve() {
int n, m;
cin >> n >> m;
bool vis[n + 1] = {false};
int b[n + 1];
vector<int>a[m + 1];
for (int i = 1; i <= n; ++i) {
cin >> b[i];
a[b[i]].push_back(i);
}
for (int i = 1; i <= n; ++i) {
if (a[b[i]].size() == 1)cout << "BeiJu\n";
else cout << a[b[i]].size() - 1 << "\n";
}
}
星期三 AcWing 3483. 2的幂次方
这道题,本质是对在二进制下的1进行递归处理
int n;
string dfs(int n) {
string str;
for (int i = 20; i >= 0; --i) {
int f = 1 << i;
if (i > 2 and (n & f)) {
str += "2(";
str += dfs(i);
str += ')';
n -= f;
if (n != 0) str += '+';
} else if (i <= 2 and (n & f)) {
if (i == 0)str += "2(0)";
if (i == 1) str += "2";
if (i == 2) str += "2(2)";
n -= f;
if (n != 0) str += '+';
}
}
return str;
}
void solve() { while (cin >> n)cout << dfs(n) << "\n";}
// 简化代码
string dfs(int x) {
if (!x)return "0";
string ans = "";
for (int j = 31; j >= 0; --j) {
if ((x >> j) & 1) ans += (j == 1) ? "2+" : "2(" + dfs(j) + ")+";
}
return ans.substr(0, ans.size() - 1);
}
void solve() {
int n;
while (cin >> n)cout << dfs(n) << "\n";
}
星期五 AcWing 3333. K-优字符串
int Case = 1;
void solve() {
int n, k, sum = 0;
cin >> n >> k;
string s;
cin >> s;
int l = 0, r = s.size() - 1;
while (l < r) {
if (s[l] != s[r])sum++;
l++, r--;
}
cout << "Case #" << Case++ << ": " << abs(sum - k) << "\n";
}
Week 3
星期一 AcWing 3554. 二进制
解法一: 模拟,二进制累加,当存在溢出时,即最后 c = 1
时要多输出一个 1
void solve() {
string a, b;
cin >> a, b = a;
int c = 0;
for (int i = 31; i >= 0; i -= 1) {
int tmp = (a[i] - '0') + c;
if (i == 31)tmp++;
a[i] = (tmp % 2) + '0';
c = tmp / 2;
}
if (c) cout << c;
cout << a << "\n";
c = 0;
for (int i = 31; i >= 0; i -= 1) {
int tmp = (b[i] - '0') + c;
if (i == 31 || i == 30)tmp++;
b[i] = (tmp % 2) + '0';
c = tmp / 2;
}
if (c)cout << c;
cout << b << "\n";
}
解法二:转换成 long long
计算
using ll = long long;
void f(ll n) {
if (n >> 32 & 1)cout << 1;//最大的32位加3会变成33位,检查一下
for (int i = 31; i >= 0; i -= 1) {
if (n >> i & 1)cout << 1;
else cout << 0;
}
cout << "\n";
}
void solve() {
string s; cin >> s;
ll n = 0;
for (int i = 0; i < 32; ++i)
if (s[i] == '1')
n = n | (1ll << (31 - i));
f(n + 1), f(n + 3);
}
解法三:Python 不讲武德
T = int(input())for _ in range(T):
n = int(input(),2)
print(bin(n+1)[2:].rjust(32, '0'))
print(bin(n+3)[2:].rjust(32, '0'))
星期二 AcWing 3565. 完美矩阵
using ll = long long;
const int N = 110;
ll a[N][N];
vector<int>e[N][N];
void solve() {
int n, m; cin >> n >> m;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)e[i][j].clear();
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) {
cin >> a[i][j];
e[min(i, n - i + 1)][min(j, m - j + 1)].push_back(a[i][j]);
}
ll ans = 0;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) {
if (!e[i][j].size())continue;
sort(e[i][j].begin(), e[i][j].end());
int t = e[i][j][(e[i][j].size() + 1) / 2 - 1];
for (int k = 0; k < e[i][j].size(); ++k)
ans += abs(t - e[i][j][k]);
}
cout << ans << "\n";
}
星期三 AcWing 3574. 乘积数量
方案一:前缀和维护区间负数个数
看到求区间的问题,会想到前缀和优化
我们可以用前缀和维护 \([1,s_n]\) 区间内的负数个数(因为会影响乘积的正负号的只有负数)
则对于区间 \([s_l,s_r]=v\),若 \(v\) 是奇数,则表示该区间的乘积为负数,反之是正数
-
若\(s_r−s_{l−1}\)为奇数,则必定是 \(s_r\) 和 \(s_{l−1}\) 一奇一偶
-
若\(s_r−s_{l−1}\)为偶数,则必定是 \(s_r\) 和 \(s_{l−1}\) 都是偶数或都是奇数
于是我们可以从前往后便利一遍数组,求出前缀和分别为奇数和偶数的个数
然后是一个组合数的问题了,设前缀和为奇数的个数为 \(v_1\) ,前缀和为偶数的个数为 \(v_2\)
区间为正数的子区间个数为 \(C^1_{v1}+C^1_{v2}\)
区间为负数的子区间个数为 \(C^1_{v1}×C^1_{v2}\)
int n;
int s[N];
int s0, s1;
void solve() {
cin >> n;
s0 ++ ;
for (int i = 1; i <= n; ++ i) {
int x;
cin >> x;
s[i] = s[i - 1] + (x < 0);
if (s[i] & 1) ++ s1;
else ++ s0;
}
cout << (LL)s0 * s1 << " " << (LL)s0 * (s0 - 1) / 2 + (LL)s1 * (s1 - 1) / 2 << endl;
}
方案二:DP
DP[i] 表示以 i 结尾的区间有多少个含奇数个负数
- 则若 xs[i] < 0,则接到 i - 1 含偶数个负数的区间上
- 否则,则接到 i - 1 含奇数个负数的区间上
但容易发现 \(dp_i\) 仅和前一项相关,所以采用滚动数组
const int N = 2e5 + 10;
int a[N], c[2];
ll cnt0, cnt1;
void solve() {
int n; cin >> n;
for (int i = 1; i <= n; ++i)
cin >> a[i];
int v = 1;
c[v]++;
for (int i = 1; i <= n; ++i) {
if (a[i] < 0) v ^= 1;
cnt0 += (ll)c[v];
cnt1 += (ll)c[v ^ 1];
c[v]++;
}
cout << cnt1 << " " << cnt0 << '\n';
}
星期四 AcWing 3580. 整数配对
没什么好讲的,排序,累加即可
void solve() {
int n; cin >> n;
int a[n + 1], cnt = 0;
for (int i = 1; i <= n; ++i)cin >> a[i];
sort(a + 1, a + 1 + n);
for (int i = 2; i <= n; i += 2)cnt += a[i] - a[i - 1];
cout << cnt ;
}
星期五 AcWing 3583. 整数分组
先对数组按升序排序,然后记录每个数符合条件的最左侧点下标,然后再用DP优化
- \(f(i,j) = max(f(i,j),f(L_i-1,j-1) + i - L[i] + 1)\)
输出 \(f[n][k]\) 即可
const int N = 5e3 + 10;
int f[N][N], n, a[N], L[N], k;
void solve() {
cin >> n >> k;
for (int i = 1; i <= n; ++i)cin >> a[i];
sort(a + 1, a + 1 + n);
for (int i = 1; i <= n; ++i) {
int p = i;
while (p > 1 && a[p - 1] >= a[i] - 5) p--;
L[i] = p;
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= k; ++j)f[i][j] = f[i - 1][j];
for (int j = 1; j <= k; ++j)
f[i][j] = max(f[i][j], f[L[i] - 1][j - 1] + i - L[i] + 1);
}
cout << f[n][k] << "\n";
}
Week 4
星期三 AcWing 3617. 子矩形计数
简单的前缀和累加
using ll = long long;
ll a[40001], b[40001], n, m, i, j, k, ans = 0;
void solve() {
cin >> n >> m >> k;
for (i = 0; i < n; i++) {cin >> a[i]; if (a[i]) a[i] += a[i - 1];}
for (i = 0; i < m; i++) {cin >> b[i]; if (b[i]) b[i] += b[i - 1];}
for (i = 1; i <= n; i++)
if (k % i == 0) {
ll u = i, v = k / i, x = 0, y = 0;
for (j = 0; j < n; j++) if (a[j] >= u) x++;
for (j = 0; j < m; j++) if (b[j] >= v) y++;
ans += x * y;
}
cout << ans;
}
星期四 AcWing 3624. 三值字符串
利用双指针维护最小长度
const int inf = 0x3f3f3f3f;
void solve() {
string s; cin >> s;
int cnt[4] = {0};
int l = 0, ans = inf;
for (int i = 0; i < (int)s.size(); ++i) {
cnt[s[i] - '0']++;
while (cnt[1] and cnt[2] and cnt[3]) {
cnt[s[l] - '0']--;
ans = min(ans, i - l + 1);
++l;
}
}
cout << (ans == inf ? 0 : ans) << "\n";
}
星期五 AcWing 3629. 同心圆涂色
模拟即可,注意 PI 的取值
const double pi = 3.1415926535897932384626433832795;
bool cmp(int a, int b) {return a > b;}
void solve() {
int n; cin >> n;
int r[n];
for (int i = 0; i < n; ++i)cin >> r[i];
sort(r, r + n, cmp);
double ans = 0.0;
int i;
for (i = 1; i < n; i += 2) {
ans += pi * (r[i - 1] * r[i - 1] - r[i] * r[i]);
}
if (i == n)ans += pi * r[i - 1] * r[i - 1];
printf("%.06lf", ans);
}
Week 5
星期一 AcWing 3636. 数组延伸
const int N = 1e5 + 10, mod = 1e9 + 7;
ll n, x;
ll a[N];
void solve() {
cin >> n >> x;
for (int i = 1; i <= n; ++i)cin >> a[i];
ll sum = 0, psum = 0;
//分别代表一开始的数组和,可以向外扩展最小次数的那个数之前的和
int cnt = N;
//最小向外拓展次数
for (int i = 1; i <= n; ++i) {
sum += a[i];
int c = 0;
for (int j = a[i]; j % x == 0; j /= x)c++;
if (c < cnt)cnt = c, psum = sum - a[i];
}
//答案就是一开始的数组和加上向外拓展次数*数组和,再加上截止到向外拓展次数最小的那个数之前的和(到这个数开始,就不是完整的拓展了)
cout << sum * (cnt + 1) + psum << endl;
}
星期二 AcWing 3646. 分水果
最少 (0,0,1) 、(0,1,0)、(1,0,0),至多 (1,1,1)
所以特判情况即可
void solve() {
int a[3];
for (int i = 0; i < 3; ++i)cin >> a[i];
int ans = 0;
if (a[0])ans++, a[0]--;
if (a[1])ans++, a[1]--;
if (a[2])ans++, a[2]--;
sort(a, a + 3);
if (a[2] and a[1])ans++, a[2]--, a[1]--;
if (a[2] and a[0])ans++, a[2]--, a[0]--;
if (a[0] and a[1])ans++, a[0]--, a[1]--;
if (a[0] and a[1] and a[2])ans++;
cout << ans << "\n";
}
星期三 AcWing 3655. 楼层
数学 or 模拟
void solve() {
int n, x;
cin >> n >> x;
if (n <= 2)cout << 1 << "\n";
else cout << (n - 2 + x - 1) / x + 1 << "\n";
// cout << ((n-3)/x+2) << endl;
}
星期五 AcWing 3664. 数组补全
贪心
找比y小的个数,一定不能超过中位数位置,然后左置1,右置y
void solve() {
int n, k, p, x, y, tot = 0, a, d = 0, i = 0, l, m;
cin >> n >> k >> p >> x >> y;
for (; i < k; tot += a, d += (a < y), i++)cin >> a;
l = n / 2 - d < n - k ? n / 2 - d : n - k;
m = n - k - l;
if (l < 0 || tot + l + m * y > x)cout << -1;
else {
while (m--)cout << y << ' ';
while (l--)cout << 1 << ' ';
}
}
Week 6
星期一 AcWing 3672. 数组重排
i - a[i] != j - a[j]
等价于 i - j != a[i] - a[j]
只需要逆序排列即可
void solve() {
int n; cin >> n;
vector<int>a(n);
for (int &x : a)cin >> x;
sort(a.begin(), a.end(), greater<int>());
for (int i = 0; i < n; ++i)cout << a[i] << " ";
cout << '\n';
}
星期二 AcWing 3679. 素数矩阵
4 1 0 0 0 0 0
0 4 1 0 0 0 0
0 0 4 1 0 0 0
0 0 0 4 1 0 0
0 0 0 0 4 1 0
0 0 0 0 0 4 1
1 0 0 0 0 0 4
void solve() {
int n; cin >> n;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
if (i == j)cout << 4 << " ";
else if (j == i + 1) cout << 1 << " " ;
else if (i == n - 1 && j == 0) cout << 1 << " ";
else cout << 0 << " ";
}
cout << endl;
}
}
星期三 AcWing 3686. 移动序列
定位区间,然后计算1,0个数
void solve() {
int n;
cin >> n;
vector<int>a(n);
int cnt1 = 0;
for (int &x : a) {
cin >> x;
if (x == 1)cnt1++;
}
int i = 0, j = n - 1;
while (a[i] == 0) i++; //第一个1的位置
while (a[j] == 0) j--; //最后一个1的位置
cout << j - i + 1 - cnt1 << '\n';
}
星期四 AcWing 3697. 回文子序列
注意:问的是回文子序列,不是子串。
长度大于等于3的回文子序列 等价于 两个相等的元素位置之差大于1
所以:遍历数组,找出两个相等的元素。如果位置之差大于1就输出 YES。
如果遍历完数组,也没找到,就输出 NO。
当然也可以用哈希表记录下元素出现的位置,在 \(O(N)\) 时间内过掉。这个方法可以看看其他题解。
void solve() {
int n; cin >> n;
vector<int>a(n);
for (int &x : a)cin >> x;
bool f = false;
for (int i = 0; i < n and !f; ++i)
for (int j = i + 1; j < n; ++j)
if (a[i] == a[j] and j - i > 1) f = true;
cout << (f ? "YES\n" : "NO\n");
}
Week 7
星期一 AcWing 3705. 子集mex值
通过维护两个变量从0开始,如果有0、1、2、3...这样的直接慢慢向上叠加
const int N = 1e5 + 100;
ll n, a[N];
void solve() {
cin >> n;
for (int i = 0; i < n; ++i)cin >> a[i];
sort(a, a + n);
ll m = 0, k = 0;
for (int i = 0; i < n; ++i) {
if (a[i] == m)m++;
else if (a[i] == k)k++;
}
cout << m + k << endl;
}
星期二 AcWing 3711. 方格涂色
待补
const int N = 1e5+10;
int main(){
int t; scanf("%d", &t);
while (t--){
int n, u, r, d, l;
scanf("%d %d %d %d %d", &n, &u, &r, &d, &l);
int top, bottom, right ,left;
bool f = 0;
top = bottom = right = left = 0;
if(u == n) top = 2;
else if(u == n-1) top = 1;
if(r == n) right = 2;
else if(r == n-1) right = 1;
if(d == n) bottom = 2;
else if(d == n-1) bottom = 1;
if(l == n) left = 2;
else if(l == n-1) left = 1;
// printf("%d %d %d %d\n",top, bottom, right, left);
for(int i = 0; i < 16; i++){
int a[4] = {0,0,0,0}, cnt = 0;
while(i>>cnt){
a[cnt] = (i>>cnt)&1;
cnt++;
}
if(a[0]+a[1] >= top && a[0]+a[1] <= u)
if(a[0]+a[2] >= left && a[0]+a[2] <= l)
if(a[1]+a[3] >= right && a[1]+a[3] <= r)
if(a[2]+a[3] >= bottom && a[2]+a[3] <= d)
f = 1;
}
if(f) cout << "YES\n";
else cout << "NO\n";
}
}
星期三 AcWing 3720. 数组重排
因为对于每个 ai+bi 都要满足 <=x 这个条件
我们只需要贪心的 使得每个和尽可能的小即可
所以对两个数组 按升序 和 降序 sort 一下即可
void solve() {
int n, x;
cin >> n >> x;
vector<int> A(n, 0), B(n, 0);
for (int i = 0; i < n; ++i)
cin >> A[i];
for (int i = 0; i < n; ++i)
cin >> B[i];
sort(A.begin(), A.end());
sort(B.begin(), B.end(), greater<int>());
bool ok = true;
for (int i = 0; i < n; ++i)
if (A[i] + B[i] > x)
ok = false;
cout << (ok ? "Yes" : "No") << endl;
}
星期四 AcWing 3725. 卖罐头
math
ll _, n, x;
void solve() {
ll l, r;
cin >> l >> r;
ll a = r + 1;
if (l % a >= (a + 1) / 2) cout << "YES\n";
else cout << "NO\n";
}
星期五 AcWing 3729. 改变数组元素
int arr[200001];
int res[200001];
void solve() {
int n, i, mn = 1e9;
cin >> n;
for (i = 1; i <= n; i++) cin >> arr[i];
for (i = n; i >= 1; i--) {
mn = min(mn, i - arr[i]);
res[i] = (mn < i);
}
for (i = 1; i <= n; i++) cout << res[i] << " ";
cout << endl;
}
Week 8
星期一 AcWing 3730. 寻找序列
由于题目保证 \(a_i \not= b_i \not = c_i\)所以直接逐位判断选什么即可,时间复杂度 \(\mathcal{O}(n)\)
void solve() {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) cin >> a[i];
vector<int> b(n);
for (int i = 0; i < n; i++) cin >> b[i];
vector<int> c(n);
for (int i = 0; i < n; i++) cin >> c[i];
vector<int> p(n, -1);
for (int i = 0; i < n; i++) {
p[i] = a[i];
if (p[i] == p[(i + 1) % n] || p[i] == p[(i + n - 1) % n]) {
p[i] = b[i];
if (p[i] == p[(i + 1) % n] || p[i] == p[(i + n - 1) % n]) {
p[i] = c[i];
}
}
}
for (int i = 0; i < n; i++) {
if (i > 0) cout << " ";
cout << p[i];
}
cout << '\n';
}
星期二 AcWing 3731. 序列凑零
ll _, n;
void solve() {
cin >> n;
int a[n + 1];
for (int i = 0; i < n; ++i) cin >> a[i];
for (int i = 0; i < n; i += 2) cout << a[i + 1] << ' ' << -a[i] << ' ';
cout << endl;
}
星期三 AcWing 3732. 矩阵复原
#include <bits/stdc++.h>
#define x first
#define y second
using namespace std;
using ll = long long;
typedef pair<int, int> PII;
const int N = 510, M = N * N;
int n, m, k;
int g[N][N];
PII pos[M];
void solve() {
cin >> n >> m;
for (int i = 1; i <= n; ++ i) {
for (int j = 1; j <= m; ++ j) {
int x;
cin >> x;
pos[x].y = j;
}
}
for (int i = 1; i <= m; ++ i) {
for (int j = 1; j <= n; ++ j) {
int x;
cin >> x;
pos[x].x = j;
}
}
for (int i = 1; i <= n * m; ++ i) {
auto &p = pos[i];
g[p.x][p.y] = i;
}
for (int i = 1; i <= n; ++ i) {
for (int j = 1; j <= m; ++ j) {
cout << g[i][j] << " ";
}
cout << endl;
}
}
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int _; for (cin >> _; _--;) solve();
return 0;
}