Manthan, Codefest 19(Div. 1 + Div. 2)
A. XORinacci
签到。
Code
/*
* Author: heyuhhh
* Created Time: 2020/2/26 9:26:33
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#define MP make_pair
#define fi first
#define se second
#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 << '\n'; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
#define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
int f[3];
int a, b, n;
void run(){
cin >> a >> b >> n;
f[0] = a, f[1] = b;
f[2] = a ^ b;
n %= 3;
cout << f[n] << '\n';
}
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;
}
B. Uniqueness
题意:
给出\(n,n\leq 2000\)个数,现在可以至多删除一段区间\([l,r]\)。最后要使得剩下的数互不相同。问最终删除区间的最小长度为多少。
思路:
枚举左、右端点,用一个\(set\)来维护剩下的元素即可。
时间复杂度\(O(n^2logn)\)。
Code
/*
* Author: heyuhhh
* Created Time: 2020/2/26 9:34:06
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#define MP make_pair
#define fi first
#define se second
#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 << '\n'; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
#define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 2000 + 5;
int n;
int a[N];
void run(){
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
set <int> s;
int ans = n;
for(int l = 0; l <= n; l++) {
if(s.count(a[l])) break;
s.insert(a[l]);
ans = min(ans, n - l);
set <int> t;
for(int r = n; r > l; r--) {
if(s.count(a[r]) || t.count(a[r])) break;
t.insert(a[r]);
ans = min(ans, r - l - 1);
}
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
run();
return 0;
}
C. Magic Grid
题意:
现在要构造一个\(n\cdot n\)的矩阵,矩阵里面的数互不相同,取值为\([0,n^2-1]\),并且使得矩阵中行、列异或值都相同。
思路:
归纳一下,直接将整个矩阵按\(k\)分块,分成多个\(4\cdot 4\)的小矩阵构造就行。
正确性因为所有的数都加上\(2^x\)过后,最后的异或值不会发生变化。
代码如下:
Code
/*
* Author: heyuhhh
* Created Time: 2020/2/26 11:02:11
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#define MP make_pair
#define fi first
#define se second
#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 << '\n'; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
#define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1000 + 5;
int n;
int a[N][N];
void run() {
int k = n / 4;
int now = 0;
for(int i = 1; i <= k; i++) {
for(int j = 1; j <= k; j++) {
for(int x = (i - 1) * 4 + 1; x <= i * 4; x++) {
for(int y = (j - 1) * 4 + 1; y <= j * 4; y++) {
a[x][y] = now++;
}
}
}
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
cout << a[i][j] << " \n"[j == n];
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
while(cin >> n) run();
return 0;
}
D. Restore Permutation
直接从后往前构造即可,利用树状数组+二分查询来确定一个位置的值,时间复杂度\(O(nlog^2n)\)。
代码如下:
Code
/*
* Author: heyuhhh
* Created Time: 2020/2/26 16:34:24
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#define MP make_pair
#define fi first
#define se second
#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 << '\n'; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
#define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 2e5 + 5;
int n;
int a[N];
ll s[N];
struct BIT {
ll c[N];
int lowbit(int x) {return x & (-x);}
void add(int x, int v) {
for(; x < N; x += lowbit(x)) c[x] += v;
}
ll query(int x) {
ll res = 0;
for(; x; x -= lowbit(x)) res += c[x];
return res;
}
}bit;
void run(){
cin >> n;
for(int i = 1; i <= n; i++) cin >> s[i];
for(int i = 1; i <= n; i++) bit.add(i, i);
for(int i = n; i >= 1; i--) {
int l = 1, r = n + 1, mid;
while(l < r) {
mid = (l + r) >> 1;
if(bit.query(mid) > s[i]) r = mid;
else l = mid + 1;
}
a[i] = l;
bit.add(a[i], -a[i]);
}
for(int i = 1; i <= n; i++) cout << a[i] << " \n"[i == n];
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
run();
return 0;
}
E. Let Them Slide
题意:
有一个\(n\cdot w,n,w\leq 10^6\)的表,现在有\(n\)个数组,每个数组中元素的取值范围为\([-10^9,10^9]\),所有数组的长度之和不超过\(10^6\)。
每一个数组可以在表内左右移动。
问最后对于每一列,其和的最大值为多少。
思路:
- 对每一个数组单独考虑其贡献:容易发现一个数组对列的贡献为\(1,2,\cdots,max,\cdots,max,max-1,\cdots,2,1\)的形式。
- 形象点来说,一个数组的贡献一开始为一个从\(1\)开始,有着最大宽度的滑动窗口;最后窗口宽度会回到\(1\)。中间有一段对列的贡献始终相同。
- 那么我们中间的那一段可以直接利用差分来处理,而前面部分和后面部分我们直接暴力计算贡献就行。用\(set\)的话好写一点,时间复杂度为\(O(len\cdot log_{len})\)。
- 注意一点细节:因为可能窗口中的最大值为负数,在某些位置,我们可以让这一列为空,那么最后的取值就是\(0\)了。对于这一点的处理可以找一下关系:设当前数组的长度为\(k\),那么\(r=w-k\),\([1,r],[w-k+1,w]\)这段可以取\(0\);我们最后窗口的上界为\(min(k,w-k+1)\)。
- 显然当\(2k\leq w\)时,所有位置均可取\(0\);
- 当\(2k>w\)即上界\(max=k\)时,此时\(r<k\),那么\([1,r]、[w-k+1,w]\)这两段区间我们暴力计算的时候可以直接计算。
最终总的时间复杂度为\(O(nlogn)\),这里的\(n\)为数组总长度。
细节见代码:
Code
/*
* Author: heyuhhh
* Created Time: 2020/2/26 17:04:19
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#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 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 << '\n'; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
#define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e6 + 5;
int n, w;
int a[N];
ll ans[N], sum[N];
void run(){
cin >> n >> w;
multiset <int> S;
for(int i = 1; i <= n; i++) {
int k; cin >> k;
for(int j = 1; j <= k; j++) cin >> a[j];
S.clear();
int Max = min(k, w - k + 1), r = w - k;
if(2 * k <= w) {
for(int j = 1; j <= k; j++) if(a[j] < 0) a[j] = 0;
}
for(int j = 1; j <= w; j++) {
if(j <= Max) {
S.insert(a[j]);
} else if(j <= min(k, w - Max + 1)) {
S.insert(a[j]);
auto it = S.lower_bound(a[j - Max]);
assert(it != S.end());
S.erase(it);
} else if(j > w - Max + 1) {
auto it = S.lower_bound(a[k - Max + j - (w - Max + 1)]);
assert(it != S.end());
S.erase(it);
} else {
int st = j;
int ed = w - Max + 1 + 1;
auto it = S.end(); it--;
int now = *it;
sum[st] += now, sum[ed] -= now;
j = w - Max + 1;
continue;
}
auto it = S.end(); it--;
int now = *it;
if(j <= r || j >= w - r + 1) now = max(0, now);
ans[j] += now;
}
assert(S.size() == 1);
}
for(int i = 1; i <= w; i++) {
sum[i] += sum[i - 1];
ans[i] += sum[i];
}
for(int i = 1; i <= w; i++) {
cout << ans[i] << " \n"[i == w];
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
run();
return 0;
}
F. Bits And Pieces
题意:
给出序列\(a_{1,2\cdots,n},n\leq 10^6\)。
现在要找最大的\(a_i|(a_j\& a_k)\),其中\((i,j,k)\)满足\(i<j<k\)。
思路:
- 显然我们可以枚举\(a_i\),那么问题就转换为如何快速找\(a_j\& a_k\)。
- 暴力的方法就是从高到低枚举二进制每一位,然后用各种方法乱搞。
- 因为最后要使得结果最大,我们二进制从高到底枚举时肯定是贪心来考虑的:即如果有两个数他们的与在这一位为\(1\),那么最后的答案中一定有这一位。
- 那么我们逐位考虑,并且考虑是否有两个在右边的数他们与的结果为当前答案的超集即可,有的话答案直接加上这一位。
- 那么可以用\(sos\ dp\)处理超集的信息,并且维护在最右端的两个位置,之后贪心来处理即可。
稍微来说一下这个\(sos\ dp\),我们定义\(dp[i][mask]\)为状态为\(mask\)时,处理了二进制后面\(i\)位的超集信息后,当前最右端的两个位置。那么转移时枚举第\(i+1\)位,若这一位为\(1\),那么\(dp[i][mask]\rightarrow dp[i+1][mask-(1<<(i+1))],dp[i+1][mask]\)进行转移即可。
代码中滚动掉了一维。
细节见代码:
Code
/*
* Author: heyuhhh
* Created Time: 2020/2/27 10:51:39
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#define MP make_pair
#define fi first
#define se second
#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 << '\n'; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
#define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 2e6 + 5;
int n;
int a[N];
pii dp[N];
void add(int x, int id) {
if(dp[x].fi == -1) {
dp[x].fi = id;
} else if(dp[x].se == -1) {
if(dp[x].fi == id) return;
dp[x].se = id;
if(dp[x].fi < dp[x].se) swap(dp[x].fi, dp[x].se);
} else if(dp[x].fi < id) {
dp[x].se = dp[x].fi;
dp[x].fi = id;
} else if(dp[x].se < id) {
if(dp[x].fi == id) return;
dp[x].se = id;
}
}
void merge(int x1, int x2) {
add(x1, dp[x2].fi);
add(x1, dp[x2].se);
}
void run() {
memset(dp, -1, sizeof(dp));
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> a[i];
add(a[i], i);
}
for(int i = 0; i < 21; i++) {
for(int j = 0; j < N; j++) if(j >> i & 1) {
merge(j ^ (1 << i), j);
}
}
int ans = 0;
for(int i = 1; i <= n - 2; i++) {
int lim = (1 << 21) - 1;
int cur = a[i] ^ lim, res = 0;
for(int j = 20; j >= 0; j--) if(cur >> j & 1) {
if(dp[res ^ (1 << j)].se > i) {
res ^= (1 << j);
}
}
ans = max(ans, res | a[i]);
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
run();
return 0;
}
重要的是自信,一旦有了自信,人就会赢得一切。