1015考试
1015考试总结
T1
题目大意:
现在有一张大小为𝑛*𝑚的网格图,小A用𝐾种颜色在网格图上作画。其中第𝑖种颜色编号为𝑖,初始时网格图中每个格子都没有颜色,编号为0。已知每种颜色小A都会使用且只使用一次,但使用的顺序是未知的。使用一种颜色时需要选定一个连续的子矩阵,将该子矩阵涂上这种颜色。后涂的颜色会覆盖之前的颜色。现在给出小A画完后的图,问有多少种颜色可能是小A最先使用的。
对于100%的数据,1≤𝑛*𝑚, 𝑘≤10^5
乱搞把。
首先特判,当图中只有一种颜色的时候,需要输出\(k - 1\)而不是\(k\),因为这种颜色肯定不是最先使用的。
考场上想出了一种很玄的做法,感觉会T,果然T了两个点,可是这种做法竟然是正解????题解就这么写的:统计每个数字的上下左右边界,然后对每个颜色扫一遍。感觉很玄啊竟然过了。
真正的正解是机房另一个dalao想出来的(%%%):还是统计每个数字的上下左右边界,然后经每个数字对应的那块矩形内都加1(初始值为0)。用二维差分来做。最后看每个一点的数值是否大于一,如果大于1,那这一个点上对应的数字肯定是覆盖了其他数字的,就计入答案,复杂度\(O(n * m)\)。
#include <bits/stdc++.h>
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == getchar()) && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 5e5 + 5, inf = 1e9;
int n, m, k, ans, num, abc[N], sum[N];
struct col { int l, r, u, d, f, vis; } a[N];
int id(int x, int y) { return (x - 1) * m + y; }
void update(int x) {
// if(a[x].u == inf || a[x].d == 0 || a[x].l == inf || a[x].r == 0) return ;
sum[id(a[x].u, a[x].l)] ++; sum[id(a[x].u, a[x].r + 1)] --; sum[id(a[x].d + 1, a[x].l)] --; sum[id(a[x].d + 1, a[x].r + 1)] ++;
}
int main() {
n = read(); m = read(); k = read();
for(int i = 0;i <= k; i++) a[i].u = a[i].l = inf;
for(int i = 1;i <= n; i++)
for(int j = 1, x;j <= m; j++) {
cin >> x;
if(a[x].vis == 0) a[x].vis = 1, num ++;
abc[id(i, j)] = x;
a[x].u = min(a[x].u, i); a[x].d = max(a[x].u, i);
a[x].l = min(a[x].l, j); a[x].r = max(a[x].r, j);
}
if(num == 1) { printf("%d", k == 1 ? 1 : k - 1); return 0; }
if(num == 0) { printf("%d", 0); return 0; }
for(int i = 1;i <= k; i++) { if(!a[i].vis) continue; update(i); }
for(int i = 1;i <= n; i++)
for(int j = 1;j <= m; j++)
sum[id(i, j)] = sum[id(i, j)] + sum[id(i - 1, j)] + sum[id(i, j - 1)] - sum[id(i - 1, j - 1)];
for(int i = 1;i <= n; i++)
for(int j = 1;j <= m; j++)
if(sum[id(i, j)] > 1) a[abc[id(i, j)]].f = 1;
for(int i = 1;i <= k; i++) if(!a[i].f) ans ++;
printf("%d", ans);
fclose(stdin); fclose(stdout);
return 0;
}
T2
题目大意:
有一个长度为𝑛的数列,数列中每个数都是[0, 𝑝−1]之间的整数。小A不知道数列中每个数的值,所以向小B做了𝑚次询问。每次小A会向小B询问一个区间[𝑙, 𝑟]中所有数的和对𝑝取模的结果。问完所有问题后,小A发现小B的回答中似乎存在矛盾。现在小A想找到最大的𝑋,满足小B的前𝑋次回答中不存在矛盾(𝑋有可能等于𝑚)。
对于100%的数据,1≤𝑛, 𝑚≤106,2≤𝑝≤10^9
带权并查集。
我们有\(m\)个这样的式子:\((b[r] - b[l - 1]) \% p = k\);
然后移项:\(b[r] \% p = (k + b[l - 1]) \% p\)。
然后用带权并查集维护差值就好了。
这里面有个图挺好的
#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 = 1e6 + 5, inf = 1e9;
int n, m, p, ans, siz[N], fa[N];
struct que { int l, r, x; } a[N];
int find(int x) {
if(fa[x] != x) {
int f = find(fa[x]);
siz[x] += siz[fa[x]];
fa[x] = f;
}
return fa[x];
}
int main() {
n = read(); m = read(); p = read(); int tag = 0;
for(int i = 1;i <= m; i++) a[i].l = read(), a[i].r = read(), a[i].x = read();
for(int i = 0;i <= n; i++) fa[i] = i;
for(int i = 1;i <= m; i++) {
int tx = find(a[i].l - 1), ty = find(a[i].r);
if(tx != ty) {
fa[tx] = ty; siz[tx] = a[i].x + siz[a[i].r] - siz[a[i].l - 1];
}
else {
if((siz[a[i].l - 1] - siz[a[i].r] + p) % p != a[i].x) { printf("%d", i - 1); tag = 1; break; }
}
}
if(!tag) printf("%d", m);
fclose(stdin); fclose(stdout);
return 0;
}
T3
题目大意:
有𝑛个炸弹分布在一条数轴上。现在需要玩家以一定的能量引爆某一个炸弹,而炸弹爆炸会引起连锁反应导致更多的炸弹爆炸。炸弹爆炸时会携带能量𝑋,可以引爆和他距离≤𝑋的所有炸弹。当一个炸弹被能量𝑋的爆炸引爆时,该炸弹会携带⌊2*𝑋/3⌋(下取整)的能量。玩家可以设置第一个引爆的炸弹的能量,现在请问,如果想引爆所有炸弹,第一个引爆的炸弹的能量最少是多少呢?
对于100%的数据,2≤𝑛≤10^6
数据结构优化线性DP。
\(f[i]\)表示将\(i\)前面所有炸弹引爆所需要的最小能量,那么状态转移方程就是:\(f[i] = min(f[i], max(3 * f[j] / 2, x[i] - x[j]))\)。
\(g[i]\)表示将\(i\)后面所有炸弹引爆所需要的最小能量,状态转移方程同理:\(g[i] = min(g[i], max(3 * g[j] / 2, x[j] - x[i]))\),记得倒叙枚举。
然后就可以得到60pts的好成绩,再用单调队列优化即可满分。
#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 = 1e6 + 5, inf = 1e9;
int n, num, ans;
int x[N], f[N], g[N], q[N];
int main() {
n = read();
for(int i = 1;i <= n; i++) x[i] = read();
sort(x + 1, x + n + 1);
int l = 1, r = 1; q[l] = 1;
for(int i = 2;i <= n; i++) {
while(l < r && max((3 * f[q[l]] + 1) / 2, x[i] - x[q[l]]) > max((3 * f[q[l + 1]] + 1) / 2, x[i] - x[q[l + 1]])) l ++;
f[i] = max((3 * f[q[l]] + 1) / 2, x[i] - x[q[l]]);
while(l <= r && max((3 * f[i] + 1) / 2, x[i + 1] - x[i]) <= max((3 * f[q[r]] + 1) / 2, x[i + 1] - x[q[r]])) r --;//这里一定要写等于号
q[++ r] = i;
}
l = 1, r = 1; q[l] = n; x[n + 1] = inf;
for(int i = n - 1;i >= 1; i--) {
while(l < r && max((3 * g[q[l]] + 1) / 2, x[q[l]] - x[i]) > max((3 * g[q[l + 1]] + 1) / 2, x[q[l + 1]] - x[i])) l ++;
g[i] = max((3 * g[q[l]] + 1) / 2, x[q[l]] - x[i]);
while(l <= r && max((3 * g[i] + 1) / 2, x[i] - x[i - 1]) <= max((3 * g[q[r]] + 1) / 2, x[q[r]] - x[i - 1])) r --;
q[++ r] = i;
}
ans = inf;
for(int i = 1;i <= n; i++) ans = min(ans, max(f[i], g[i]));
printf("%d", ans);
return 0;
}