1012模拟赛复盘
考试安排
8:00 - 8:30:看T1,没有太多思路(实际就是套路题),一直在完善我的思路
8:50 - 9:10:战略放弃T1, 开T2,先打50pts的暴力,等会拍用
9:20 - 9:50 :爆炸,有细节错误, 调
9:50-10:10:调出来了,直接看T4
10:10 - 10:20 :迅速码完T4 35pts
10:10 - 10:50 : 重看T1,有思路了,开码
10:50 - 12:00 :调, 调, 调出来了
预期分数: 100 + 50 + 0 + 60 = 210
分数: 0 + 0 + 0 + 60 = 60pts
出现问题
1.不懂得取舍, 在T1没有想到非常容易得实现方式时就匆匆开打
2.后面的暴力分没有保证完全拿到
3.心态问题,T1在前1个小时打不出来就爆,感觉是一个很大的问题
解决or收获?
T1一般不考察难的知识点,要仔细思考题目的性质,或者反着做,技巧性的东西一定要记清
题目的部分分不一定是递增的,本题的80分感觉比T3的80分好写多了
要稳住心态,不管T1有没有过掉,暴力拿满分数一定不低
新涂色游戏
回头看这道题其实非常简单
对于涂色,整行或整列,则最后一定有一整行或一整列为一个颜色,反着做,再将操作reverse就可以了
//正着做不好做,反着删
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define PII pair<int, int>
const int MAX = 1100;
int n, a[MAX][MAX], tot;
bool can[2 * MAX];
int num[2 * MAX][2 * MAX], kind[2 * MAX];
PII ans[2 * MAX];
void work(int id) {
if(id <= n) {
int co;
for(int i = 1; i <= n; i++) {
if(a[id][i] != 0 && a[id][i] != -1) {
co = a[id][i];
num[n + i][a[id][i]]--;
if(num[n + i][a[id][i]] == 0) kind[n + i] -= 1;
a[id][i] = 0;
}
}
ans[++tot] = {(PII){id, co}};
} else {
int co;
id = (id % n == 0) ? (n) : (id % n);
for(int i = 1; i <= n; i++) {
if(a[i][id] != 0 && a[i][id] != -1) {
co = a[i][id];
num[i][a[i][id]]--;
if(num[i][a[i][id]] == 0) kind[i] -= 1;
a[i][id] = 0;
}
}
ans[++tot] = {(PII){id + n, co}};
}
}
int main() {
freopen("game.in","r",stdin);
scanf("%d", &n);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++) {
scanf("%d", &a[i][j]);
if(a[i][j] == 0) a[i][j] = -1;
}
for(int i = 1; i <= n; i++) {
bool flg = 1;
for(int j = 1; j <= n; j++) {
if(a[i][j] == -1) {
flg = 0;
break;
} else {
if(num[i][a[i][j]] == 0) kind[i]++;
num[i][a[i][j]]++;
}
}
can[i] = flg;
}
for(int j = 1; j <= n; j++) {
bool flg = 1;
for(int i = 1; i <= n; i++) {
if(a[i][j] == -1) {
flg = 0;
break;
} else {
if(num[n + j][a[i][j]] == 0) kind[n + j]++;
num[n + j][a[i][j]]++;
}
}
can[n + j] = flg;
}
for(int i = 1; i <= 2 * n; i++) {
for(int j = 1; j <= 2 * n; j++) {
if(can[j] == 1 && kind[j] == 1) {
work(j);
can[j] = 0;
break;
}
}
}
reverse(ans + 1, ans + 1 + tot);
printf("%d\n", tot);
for(int i = 1; i <= tot; i++) {
printf("%d %d\n", ans[i].first, ans[i].second);
}
return 0;
}
新-滑动窗口
典中典,
∑
l
i
≤
1
0
6
\sum li \le10^{6}
∑li≤106对于滑块顶到最左边与滑块顶到最右边相交的直接暴力做,如果不交,直接差分最大值,细节较多
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int MAX = 1e6 + 70;
int n, w, l[MAX];
LL ans[MAX], cha[MAX];
LL a[MAX],b[MAX]; //统计
deque<LL> q;
void work(int h) {
LL maxx = 0;
vector<LL> Now;
Now.clear();
Now.push_back(0);
for(int j = 1; j <= l[h]; j++) {
LL x; scanf("%lld", &x);
b[j] = x;
maxx = max(maxx, x);
Now.push_back(x);
}
if(l[h] < w - l[h] + 1) { //不交
LL Max = 0;
for(int i = 1; i <= l[h]; i++) {
Max = max(Max, Now[i]);
ans[i] += Max;
}
Max = 0;
for(int i = w; i >= w - l[h] + 1; i--) {
Max = max(Max, Now[l[h] - (w - i)]);
ans[i] += Max;
}
cha[l[h] + 1] += maxx;
cha[w - l[h] + 1] -= maxx;
} else { //香蕉
memset(a, 0xcf, sizeof(a));
while(!q.empty()) q.pop_back();
int len = w - l[h] + 1;
for(int i = 1; i <= l[h]; i++) {
while(!q.empty() && i - q.front() + 1 > len) q.pop_front();
while(!q.empty() && Now[q.back()] < Now[i]) q.pop_back();
q.push_back(i);
a[i] = max(a[i], Now[q.front()]);
}
for(int i = 1; i <= w - l[h]; i++) a[i] = max(a[i], 1LL * 0);
while(!q.empty()) q.pop_back();
Now.clear();
for(int i = 0; i <= w - l[h]; i++) Now.push_back(0);
for(int i = w - l[h] + 1; i <= w; i++) Now.push_back(b[i - (w - l[h])]);
for(int i = w; i >= w - l[h] + 1; i--) {
while(!q.empty() && q.front() - i + 1 > len) q.pop_front();
while(!q.empty() && Now[q.back()] < Now[i]) q.pop_back();
q.push_back(i);
a[i] = max(a[i], Now[q.front()]);
}
for(int i = l[h] + 1; i <= w; i++) {
a[i] = max(a[i], 1LL * 0);
}
for(int i = 1; i <= w; i++) {
ans[i] += a[i];
}
}
}
int main() {
freopen("windows.in","r",stdin);
scanf("%d%d", &n, &w);
for(int i = 1; i <= n; i++) {
scanf("%d", &l[i]);
work(i);
}
for(int i = 1; i <= w; i++) {
cha[i] += cha[i - 1];
printf("%lld ", ans[i] + cha[i]);
}
return 0;
}
/*
1 5
2 -10 10
*/
小明去旅游
目前还不会,先锅着
Heavy and Frail
35pts,二进制分组背包暴力跑
80pts? 发现只有单点修改,其他不变,跑一个前缀背包,跑一个后缀背包,对于查询,将前后两个背包合并,再插入
//根据m非常小的性质, 且是单点修改, 完全可以维护前i个数的背包,与后i个数的背包, 查询时暴力合并,复杂度 m*m*q * logc
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int MAX = 5100;
int n, m, q;
LL val[MAX], v[MAX], num[MAX];
LL f_pre[MAX][810], f_back[MAX][810]; //表示前i个数的背包, 后i个数的背包
LL f[810], ans[MAX * 10];
struct made {
int id;
LL x, y, z;
int whr;
}ask[MAX * 10];
bool mycmp(made X, made Y) {
return X.id < Y.id;
}
int main() {
freopen("reflect.in","r",stdin);
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) scanf("%lld",&val[i]);
for(int i = 1; i <= n; i++) scanf("%lld", &v[i]);
for(int i = 1; i <= n; i++) scanf("%lld", &num[i]);
scanf("%d", &q);
for(int i = 1; i <= q; i++) {
scanf("%d%lld%lld%lld", &ask[i].id, &ask[i].x, &ask[i].y, &ask[i].z);
ask[i].whr = i;
}
sort(ask + 1, ask + 1 + q, mycmp);
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) f_pre[i][j] = f_pre[i - 1][j];
int Num = num[i], k = 1;
while(k <= Num) {
LL V = k * v[i]; LL VAL = k * val[i];
for(int j = m; j >= V; j--) f_pre[i][j] = max(f_pre[i][j], f_pre[i][j - V] + VAL);
Num -= k;
k *= 2;
}
if(Num != 0) {
LL V = Num * v[i]; LL VAL = Num * val[i];
for(int j = m; j >= V; j--) f_pre[i][j] = max(f_pre[i][j], f_pre[i][j - V] + VAL);
}
}
for(int i = n; i >= 1; i--) {
for(int j = 1; j <= m; j++) f_back[i][j] = f_back[i + 1][j];
int Num = num[i], k = 1;
while(k <= Num) {
LL V = k * v[i]; LL VAL = k * val[i];
for(int j = m; j >= V; j--) f_back[i][j] = max(f_back[i][j], f_back[i][j - V] + VAL);
Num -= k;
k *= 2;
}
if(Num != 0) {
LL V = Num * v[i]; LL VAL = Num * val[i];
for(int j = m; j >= V; j--) f_back[i][j] = max(f_back[i][j], f_back[i][j - V] + VAL);
}
}
for(int i = 1; i <= q; i++) {
for(int j = 1; j <= m; j++) f[j] = 0;
for(int j = 1; j <= m; j++) {
for(int k = 0; k <= j; k++) {
f[j] = max(f[j], f_pre[ask[i].id - 1][j - k] + f_back[ask[i].id + 1][k]);
}
}
int Num = ask[i].z, k = 1;
while(Num >= k) {
LL V = ask[i].y * k; LL VAL = ask[i].x * k;
for(int j = m; j >= V; j--) f[j] = max(f[j], f[j - V] + VAL);
Num -= k;
k *= 2;
}
if(Num) {
LL V = ask[i].y * Num; LL VAL = ask[i].x * Num;
for(int j = m; j >= V; j--) f[j] = max(f[j], f[j - V] + VAL);
}
ans[ask[i].whr] = f[m];
}
for(int i = 1; i <= q; i++) printf("%lld\n", ans[i]);
return 0;
}
100pts,只有单点修改,前后缀的合并
m
2
m^2
m2没有拓展性,思考如果分治去做,
(
l
,
r
)
(l, r)
(l,r)代表到
(
l
,
r
)
(l,r)
(l,r)区间内除了
(
l
,
r
)
(l,r)
(l,r)都已经被加入背包,到
(
x
,
x
)
(x, x)
(x,x)时便统计答案,时间复杂度
O
(
n
∗
m
∗
l
o
g
n
2
+
m
q
)
O(n*m*log_n^2+mq)
O(n∗m∗logn2+mq)
重复计算,分治
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int MAX = 5010;
int n, m, q;
LL val[MAX], v[MAX], num[MAX], ans[MAX * 10];
LL f[900];
struct made {
int id;
LL num, v, val;
};
vector<made> ask[MAX];
void Nowwork(int l, int r) {
for(int j = l; j <= r; j++) {
int Num = num[j], k = 1;
while(k <= Num) {
LL V = k * v[j], VAL = val[j] * k;
for(int j = m; j >= V; j--) f[j] = max(f[j], f[j - V] + VAL);
Num -= k;
k *= 2;
}
if(Num) {
LL V = Num * v[j], VAL = val[j] * Num;
for(int j = m; j >= V; j--) f[j] = max(f[j], f[j - V] + VAL);
}
}
}
void work(int x) {
LL fnow[802];
for(auto y : ask[x]) {
int Num = y.num, k = 1;
for(int i = 0; i <= m; i++) fnow[i] = f[i];
while(k <= Num) {
LL V = k * y.v, VAL = k * y.val;
for(int i = m; i >= V; i--) f[i] = max(f[i - V] + VAL, f[i]);
Num -= k;
k *= 2;
}
if(Num) {
LL V = Num * y.v, VAL = Num * y.val;
for(int i = m; i >= V; i--) f[i] = max(f[i - V] + VAL, f[i]);
}
ans[y.id] = f[m];
for(int i = 0; i <= m; i++) f[i] = fnow[i];
}
}
void slove(int l, int r) {
if(l == r) {
work(l);
return ;
}
int mid = (l + r) >> 1;
LL fnow[810];
for(int i = 0; i <= m; i++) fnow[i] = f[i];
Nowwork(mid + 1, r);
slove(l, mid);
for(int i = 0; i <= m; i++) f[i] = fnow[i];
Nowwork(l, mid);
slove(mid + 1, r);
}
int main() {
freopen("reflect.in","r",stdin);
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) scanf("%lld", &val[i]);
for(int i = 1; i <= n; i++) scanf("%lld", &v[i]);
for(int i = 1; i <= n; i++) scanf("%lld", &num[i]);
scanf("%d", &q);
for(int i = 1; i <= q; i++) {
int t; LL x, y, z; scanf("%d%lld%lld%lld", &t, &x, &y, &z);
made Now; Now.id = i, Now.num = z, Now.v = y, Now.val = x;
ask[t].push_back(Now);
}
slove(1, n);
for(int i = 1; i <= q; i++) {
printf("%lld\n", ans[i]);
}
return 0;
}