题目链接
显然,根据兼或和的性质,当 l 和 r 分别为 1 和 n 时最后的结果最大。
#include <bits/stdc++.h>
using namespace std;
long long n, a, b, sa, sb;
int main() {
scanf("%d", &n);
sa = sb = 0;
for(int i = 0; i < n; i++) {
scanf("%I64dd", &a);
sa |= a;
}
for(int i = 0; i < n; i++) {
scanf("%I64d", &b);
sb |= b;
}
printf("%I64d\n", sa + sb);
return 0;
}
显然,这题直接暴力是不行的。在不算主算法的情况下,输入的复杂度是 O(k) ,输出的复杂度是 O(nm) 。这就表示本题仅允许我们在输入的同时维护一些量,然后在输出的时候,对矩阵的每一个元素,根据这些量来决定元素的值。对于一个元素 (i,j) 而言,若要知道它最终的值是多少,仅需知道它最终被刷上什么颜色,也就是说,仅需知道它所在的行的最终颜色,它所在的列的最终颜色,以及这两个颜色出现的先后顺序。总而言之,我们需要对每一行,每一列维护最后被涂上的颜色和被涂上的时间。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e3 + 5;int n, m, k, a, b, c;
int cr[maxn], cc[maxn], pr[maxn], pc[maxn];
int G[maxn][maxn];
int main() {
scanf("%d%d%d", &n, &m, &k);
for(int i = 1; i <= k; i++) {
scanf("%d%d%d", &a, &b, &c);
if(a == 1) {
cr[b] = c;
pr[b] = i;
}
else {
cc[b] = c;
pc[b] = i;
}
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
G[i][j] = pr[i] > pc[j] ? cr[i] : cc[j];
}
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
printf("%d ", G[i][j]);
}
puts("");
}
return 0;
}
本题用暴力的方法肯定也是不行的了。既然题目给的每次排序的区间是前缀,那么就应该充分利用前缀的性质。不难发现,对 [0,r] 这个前缀排序以后,在这个排序之前的对长度小于r的前缀的排序都无效了。所以有效的前缀的长度应该是递减的。那么我们可以用数组模拟的单调队列来存储这些递减的前缀。然后对这些前缀依次排序就可以常数地优化时间。不过可惜的是,这样的优化仍然不够。现假设已经对长度为 ri 的前缀排序完毕。在对长度为 ri+1 的前缀排序前,我们首先可以将 [ri+1+1,ri] 的排序结果的出来,因为以后的排序不会再影响这个区间内的元素的顺序。于是对 i 循环即可得到全部结果。注意,每次排序不能真的去排序,我们应该用 bl,br 两个指针来维护剩余的区间,然后根据 ti 来决定取数是从区间的哪头开始。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;
int n, m, cnt, bl, br, a[maxn], b[maxn], t[maxn], r[maxn];
int main() {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
cnt = 0;
for(int ti, ri; m--;) {
scanf("%d%d", &ti, &ri);
while(cnt && ri > r[cnt-1]) {
cnt--;
}
t[cnt] = ti;
r[cnt++] = ri;
}
for(int i = 1; i <= r[0]; i++) {
b[i] = a[i];
}
sort(b + (bl = 1), b + (br = r[0]) + 1);
r[cnt++] = 0;
for(int i = 0; i < cnt; i++) {
for(int j = r[i-1]; j > r[i]; j--) {
a[j] = t[i-1] == 1 ? b[br--] : b[bl++] ;
}
}
for(int i = 1; i <= n; i++) {
printf("%d ", a[i]);
}
return 0;
}
将字符串“解压”后KMP匹配显然是会超时的。于是考虑能否直接用压缩后的信息来匹配。于是可以将压缩后的串当成“字符串”,对于在模式“字符串”两头的“字符” c−l ,只要在文本串中存在 c0−l0 ,满足 c0=c 且 l0≥l 就表示模式串和文本串中的这两个“字符”相等。对于在模式串中间(不在两端)的“字符” c−l ,在文本串中存在 c0−l0 ,必须满足 c=c0 , l=l0 才能表示“字符”相等。如此,将结构体 (c,l) 当成字符,进行KMP匹配,统计匹配数量就能得到答案。注意要将输入中相同的“字符”合并,并且要特判只有一个“字符”的情况。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair <ll, char> p;
const int maxn = 2e5 + 5;
int n, m, next[maxn];
ll ans;
vector <p> v[2];
void input(int n, int d) {
char ch;
int num;
for(int i = 0; i < n; i++) {
scanf("%d-%c", &num, &ch);
if(v[d].size() && v[d].back().second == ch) {
v[d].back().first += num;
}
else v[d].push_back(p(num, ch));
}
}
void getNext() {
int j, k;
next[j=0] = k = -1;
while(j < v[1].size()) {
if(k == -1 || v[1][k] == v[1][j]) {
next[++j] = ++k;
}
else k = next[k];
}
}
bool judge(p x, p y) {
return x.first >= y.first && x.second == y.second;
}
void kmpCount() {
int j = 0;
for(int i = 0; i < v[0].size(); i++) {
if(j == v[1].size() - 1 && judge(v[0][i], v[1][j])) {
j++;
}
else {
while(j && v[0][i] != v[1][j]) {
j = next[j];
}
if(v[0][i] == v[1][j] || !j && judge(v[0][i], v[1][j])) {
j++;
}
}
if(j == v[1].size()) {
j = next[j];
ans++;
}
}
}
int main() {
scanf("%d%d", &n, &m);
input(n, 0);
input(m, 1);
if(v[1].size() > 1) {
getNext();
kmpCount();
}
else for(int i = 0; i < v[0].size(); i++) {
if(!judge(v[0][i], v[1][0])) continue;
ans += (v[0][i].first - v[1][0].first + 1);
}
printf("%I64d\n", ans);
return 0;
}