1017考试总结
1017考试总结
T1
题目大意:
一个长度为n的非负整数序列x,满足m个条件:第i个条件为\(x[li]\ or \ x[li+1]or...or\ x[ri]=pi\)。他想知道是否存在一个序列满足条件,如果存在,他还要构造出一个这样的序列。如果存在这样的序列x,第一行输出Yes,第二行输出n个不超过2^30-1的非负整数表示x[1]~x[n],否则输出一行No。对于100%的数据,n,m≤100,000,1 ≤ li ≤ ri ≤ n,0 ≤ pi < 2 ^ 30。
二进制。
我们考虑在二进制上一位一位的搞。对于一个条件\([l_i, r_i] p_i\),假设\(p_i\)第\(k\)位上为0,那么这个区间里所有数的第\(k\)位上都是0,我们把\(f[l_i ...r_i][k] ++\)。如果说\(f[i][j] > 0\),这说明\(x[i]\)这个数第\(j\)位上一定为0,我们让\(num[i][j] = 0\),如果\(f[i][j] == 0\),那么\(num[i][j] = 1\)。
这样每个数就都可以表示出来了。
那怎么判断无解呢?用\(sum[i][j]\)表示前\(i\)个数第\(j\)位上1的个数,对于每一个条件\([l_i, r_i] p_i\),判断\(sum[r_i][j] - sum[l_i - 1][j]\)与\(p_i\)的第\(j\)位是否匹配就好了。
#include <bits/stdc++.h>
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 1e5 + 5;
int n, m, tag;
int f[N][31], sum[N][31], num[N][31];
struct line { int l, r, p; } a[N];
void calc_f(int l, int r, int p) {
for(int i = 0;i <= 30; i++) {
if(!((p >> i) & 1)) f[l][i] ++, f[r + 1][i] --;
}
}
int judge(int l, int r, int p) {
for(int i = 0;i <= 30; i++) {
int tmp = sum[r][i] - sum[l - 1][i];
if((!((p >> i) & 1) && tmp > 0) || (((p >> i) & 1) && tmp == 0)) return 0;
}
return 1;
}
int calc_num(int x) {
int res = 0;
for(int i = 0;i <= 30; i++) {
if(num[x][i] == 1) res += (1 << i);
}
return res;
}
int main() {
n = read(); m = read();
for(int i = 1;i <= m; i++) {
a[i].l = read(), a[i].r = read(), a[i].p = read();
calc_f(a[i].l, a[i].r, a[i].p);
}
for(int i = 0;i <= 30; i++)
for(int j = 1;j <= n; j++) f[j][i] += f[j - 1][i];
for(int i = 0;i <= 30; i++)
for(int j = 1;j <= n; j++) {
if(f[j][i] > 0) num[j][i] = 0;
else num[j][i] = 1;
}
for(int i = 0;i <= 30; i++)
for(int j = 1;j <= n; j++)
sum[j][i] = sum[j - 1][i] + num[j][i];
for(int i = 1;i <= m; i++)
if(!judge(a[i].l, a[i].r, a[i].p)) { tag = 1; break; }
if(tag) printf("No\n");
else {
printf("Yes\n");
for(int i = 1;i <= n; i++) printf("%d ", calc_num(i));
}
return 0;
}
T2
题目大意:
两个字符串s,t,其中s只包含小写字母以及\(*\),t只包含小写字母。可以进行任意多次操作,每次选择s中的一个\(*\),将它修改为任意多个(可以是0个)它的前一个字符。他想知道是否能将s修改为t。如果能将s修改为t,输出Yes,否则输出No。对于100%的数据,T≤100,|s|,|t|≤30000。
双指针 + 模拟。
用两个指针\(t, k\)表示当先字符串\(s, t\)枚举到了那个字符。
如果当前枚举到的两个字符不相等,输出No。如果当前枚举到的\(s\)的字符是\(x\), \(x\)的下一个字符是\(*\),直接跳过,一直跳到连续的最后一个\(x\),并且统计\(x\)的个数\(num1\),同样的字符串\(t\)也要往后跳,也是跳到连续的最后一个\(x\),统计\(x\)的个数\(num2\)。如果\(num1 > num2\),输出No,否则往下找,重复这个过程。
#include <bits/stdc++.h>
using namespace std;
int T, n, m;
char a[30005], b[30005];
int main() {
cin >> T;
while(T --> 0) {
cin >> a >> b;
n = strlen(a); m = strlen(b);
int t = 0, k = 0, flag = -1;
while(t <= n || k <= m) {
if(a[t] != b[k]) {
printf("No\n"); flag = 1; break;
}
else {
int num1 = 0, num2 = 0;
if(a[t + 1] == '*') {
char ch = a[t];
while(t <= n && (a[t + 1] == '*' || a[t + 1] == ch)) {
if(a[t + 1] == ch) num1 ++; t++;
}
while(k <= m && b[k + 1] == ch) k ++, num2 ++;
if(num1 > num2) {
printf("No\n"); flag = 1; break;
}
}
}
t ++; k ++;
}
if(flag == -1) printf("Yes\n");
}
return 0;
}
T3
题目大意:
一个3×n的数组,要在每一列选一个数(或者不选),满足以下条件:
(1)如果在第一行选,那它必须大于等于上一个数
(2)如果在第二行选,那么必须小于等于上一个数
(3)如果在第三行选,对于连续的一段在第三行选的数,必须满足方向相同(都小于等于上一个数或者都大于等于上一个数)。
对于100%的数据, n <= 100000, m <= 1000000000
洛谷链接
数据结构优化DP。
我们先设4种状态:1:选第一行的数;2:选第二行的数;3:选第三行的数并且单调不降;4:选第三行的数并且单调不升。
\(dp[i][j]\)表示第\(j\)列状态为\(i\)的最大合法长度:那么状态转移方程是:\(dp[i][j] = max(dp[i'][k] + 1)\)。
\(k < i\),对于\(i = 1, 2\)时,\(i' = 1, 2, 3, 4\)都可以;对于\(i = 3\)时,\(i' = 1, 2, 3\);对于\(i = 4\)时,\(i' = 1, 2, 4\)。
暴力枚举\(k\)是\(O(n ^ 2)\)的,用线段树维护最大值则可以优化到\(O(n log n)\)。
#include <bits/stdc++.h>
#define ls(o) (o << 1)
#define rs(o) (o << 1 | 1)
#define mid ((l + r) >> 1)
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 1e5 + 5;
int n, cnt, ans;
int a[N], b[N], c[N], disc[N << 2];
int dp[5][N], t[5][N << 4];
void insert(int o, int l, int r, int x, int val, int id) {
if(l == r) { t[id][o] = max(t[id][o], val); return ; }
if(x <= mid) insert(ls(o), l, mid, x, val, id);
if(x > mid) insert(rs(o), mid + 1, r, x, val, id);
t[id][o] = max(t[id][ls(o)], t[id][rs(o)]);
}
int query(int o, int l, int r, int x, int y, int id) {
if(x <= l && y >= r) { return t[id][o]; }
int res = 0;
if(x <= mid) res = max(res, query(ls(o), l, mid, x, y, id));
if(y > mid) res = max(res, query(rs(o), mid + 1, r, x, y, id));
return res;
}
int main() {
n = read();
for(int i = 1;i <= n; i++) a[i] = read(), disc[++ cnt] = a[i];
for(int i = 1;i <= n; i++) b[i] = read(), disc[++ cnt] = b[i];
for(int i = 1;i <= n; i++) c[i] = read(), disc[++ cnt] = c[i];
sort(disc + 1, disc + 1 + cnt);
cnt = unique(disc + 1, disc + cnt + 1) - disc - 1;
for(int i = 1;i <= n; i++) a[i] = lower_bound(disc + 1, disc + cnt + 1, a[i]) - disc;
for(int i = 1;i <= n; i++) b[i] = lower_bound(disc + 1, disc + cnt + 1, b[i]) - disc;
for(int i = 1;i <= n; i++) c[i] = lower_bound(disc + 1, disc + cnt + 1, c[i]) - disc;
for(int i = 1;i <= n; i++) {
dp[1][i] = max(dp[1][i], query(1, 1, cnt, 1, a[i], 1));
dp[1][i] = max(dp[1][i], query(1, 1, cnt, 1, a[i], 2));
dp[1][i] = max(dp[1][i], query(1, 1, cnt, 1, a[i], 3));
dp[1][i] = max(dp[1][i], query(1, 1, cnt, 1, a[i], 4));
dp[1][i] ++;
dp[2][i] = max(dp[2][i], query(1, 1, cnt, b[i], cnt, 1));
dp[2][i] = max(dp[2][i], query(1, 1, cnt, b[i], cnt, 2));
dp[2][i] = max(dp[2][i], query(1, 1, cnt, b[i], cnt, 3));
dp[2][i] = max(dp[2][i], query(1, 1, cnt, b[i], cnt, 4));
dp[2][i] ++;
dp[3][i] = max(dp[3][i], query(1, 1, cnt, 1, c[i], 1));
dp[3][i] = max(dp[3][i], query(1, 1, cnt, 1, c[i], 2));
dp[3][i] = max(dp[3][i], query(1, 1, cnt, 1, c[i], 3));
dp[3][i] ++;
dp[4][i] = max(dp[4][i], query(1, 1, cnt, c[i], cnt, 1));
dp[4][i] = max(dp[4][i], query(1, 1, cnt, c[i], cnt, 2));
dp[4][i] = max(dp[4][i], query(1, 1, cnt, c[i], cnt, 4));
dp[4][i] ++;
insert(1, 1, cnt, a[i], dp[1][i], 1); insert(1, 1, cnt, b[i], dp[2][i], 2);
insert(1, 1, cnt, c[i], dp[3][i], 3); insert(1, 1, cnt, c[i], dp[4][i], 4);
}
for(int i = 1;i <= n; i++)
for(int j = 1;j <= 4; j++)
ans = max(ans, dp[j][i]);
printf("%d", ans);
return 0;
}