最近太懒了。。ABCD直接跳过吧。。。
E2.Three Blocks Palindrome (hard version)
题意:
定义合法的序列类似于这样:
其中(x,y)可以为(0)。
现给出序列(a_{1,2,cdots,n},a_ileq 200),要求找到长度最大的合法子序列,输出最长长度。
思路:
记(pre_{i,j})为(a_k=j,kleq i)的个数(即一个前缀和)。
那么考虑暴力方法:
- 枚举(a,x),然后枚举(b),利用前缀和快速统计答案。
但复杂度为(O(200cdot 200cdot n)),并不能卡过去。
但是注意到枚举(x)时只与(a)所在的位置有关,也就是说我们不需要枚举所有的(n),而只枚举所有(a)在的位置,这样时间复杂度就降为(O(200cdot n))。
具体的实现方法用一个vector来记录每个数所在的位置即可。
详见代码:
Code
/*
* Author: heyuhhh
* Created Time: 2020/4/14 12:59:52
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << std::endl; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
template <template<typename...> class T, typename t, typename... A>
void err(const T <t> &arg, const A&... args) {
for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
#define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 2e5 + 5, M = 205;
int n;
int a[N];
int pre[N][M];
void run() {
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
vector <vector<int>> at(M);
for(int j = 1; j <= 200; j++) {
for(int i = 1; i <= n; i++) {
pre[i][j] = pre[i - 1][j] + (a[i] == j);
if(a[i] == j) at[j].push_back(i);
}
}
int ans = 0;
for(int i = 1; i <= 200; i++) {
for(int l = 0; l < sz(at[i]); l++) {
int r = sz(at[i]) - 1 - l;
if(l > r) break;
if(l == r) {
ans = max(ans, sz(at[i]));
} else {
int Max = 0;
for(int j = 1; j <= 200; j++) {
Max = max(Max, pre[at[i][r] - 1][j] - pre[at[i][l]][j]);
}
ans = max(ans, 2 * (l + 1) + Max);
}
}
}
cout << ans << '
';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
int T; cin >> T;
while(T--) run();
return 0;
}
F. Robots on a Grid
题意:
给出一个(ncdot m,ncdot mleq 10^6)的网格,每个格子中存在一个方向,如果一个机器人位于当前格子,那么就会朝指定方向走一步,保证输入数据不会使机器人撞墙。同时每个格子还有一种颜色(黑或者白)。
现在你要在一些格子上面放置一些机器人,要求在任一时刻,两两机器人都不同。
现在回答最多能够在网格中放置多少机器人以及在满足数量最多的条件下,将机器人放置在黑色格子的最大数量为多少。
思路:
简单粗暴的方法:注意到这个网格所形成的图具有以下性质:
- 每个结点出度为(1)。
那么这张图会形成若干个内向基环树,对于每一个基环树,显然我们要使得所有的机器人在环上即可使得总数量最大。这可以通过拓扑排序找出所有的环,第一问的答案即位环长的总和。
对于第二问,我们选择机器人的初始位置可能并不在环上,但最终要走到环。这里我们从环上某一点出发在反图上(bfs)求距离,那么我们将基环树中的每个结点到源点的距离(dist_i)按照模(len)的余数分类,此时不同类的结点走不到彼此的结点。我们贪心在每一类中看看是否存在黑点即可。
意识流简要证明如下:
假设我们已经走了(x,len|x)步,(x)很大很大,使得所有的结点都已在环上。显然此时每类点到源点的距离不同。并且此时将所有的点后退若干步都不会发现冲突(考虑要么出环,要么在环上不同位置两种情况)。
证毕。
细节见代码:
Code
/*
* Author: heyuhhh
* Created Time: 2020/4/14 9:05:02
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <numeric>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << std::endl; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
template <template<typename...> class T, typename t, typename... A>
void err(const T <t> &arg, const A&... args) {
for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
#define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, -1, 1};
void run() {
int n, m; cin >> n >> m;
vector <string> c(n), mp(n);
vector <vector<int>> deg(n, vector<int>(m)), dist(n, vector<int>(m, -1));
vector <vector<pii>> to(n, vector<pii>(m));
for(int i = 0; i < n; i++) cin >> c[i];
for(int i = 0; i < n; i++) {
cin >> mp[i];
for(int j = 0; j < m; j++) {
if(mp[i][j] == 'U') ++deg[i - 1][j], to[i][j] = MP(i - 1, j);
if(mp[i][j] == 'D') ++deg[i + 1][j], to[i][j] = MP(i + 1, j);
if(mp[i][j] == 'L') ++deg[i][j - 1], to[i][j] = MP(i, j - 1);
if(mp[i][j] == 'R') ++deg[i][j + 1], to[i][j] = MP(i, j + 1);
}
}
vector <pii> que;
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
if(deg[i][j] == 0) que.push_back(MP(i, j));
}
}
for(int i = 0; i < sz(que); i++) {
int x = que[i].fi, y = que[i].se;
int nx = x, ny = y;
if(mp[x][y] == 'U') --nx;
if(mp[x][y] == 'D') ++nx;
if(mp[x][y] == 'L') --ny;
if(mp[x][y] == 'R') ++ny;
if(--deg[nx][ny] == 0) {
que.push_back(MP(nx, ny));
}
}
int ans1 = 0, ans2 = 0;
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) if(deg[i][j] > 0 && dist[i][j] == -1) {
int len = 0;
que.clear();
que.push_back(MP(i, j));
dist[i][j] = 0;
for(int t = 0; t < sz(que); t++) {
int x = que[t].fi, y = que[t].se;
if(deg[x][y] > 0) ++len;
for(int k = 0; k < 4; k++) {
int nx = x + dx[k], ny = y + dy[k];
if(nx >= 0 && nx < n && ny >= 0 && ny < m && dist[nx][ny] == -1 && to[nx][ny] == MP(x, y)) {
que.push_back(MP(nx, ny));
dist[nx][ny] = dist[x][y] + 1;
}
}
}
vector <int> f(len);
for(auto it : que) {
if(c[it.fi][it.se] == '0') f[dist[it.fi][it.se] % len] = 1;
}
ans1 += len;
ans2 += accumulate(all(f), 0);
}
}
cout << ans1 << ' ' << ans2 << '
';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
int T; cin >> T;
while(T--) run();
return 0;
}
当然根据上面的想法,我们可以直接让每个点走无穷多步,那么之后所有相同类的点都在一个格子中,并且这个格子是环中一点。
答案即为有多少个等价类和每一个类中是否有黑色格子即可。
走无穷多步可以通过倍增/矩阵快速幂等实现。
这种方法写起来十分简单,不过我懒得写了。