GRYZ20211102模拟赛解题报告
期望得分:\(0 \sim 100+100+30 = 230pts\)
实际得分:\(10 + 60 + 10 = 80pts\)
评价一下这套题。
@Suzt_ilymtics
什么垃圾模拟题题面描述不清啊!出大模拟还不给大样例做nm 啊! /fn
出题人你没有妈妈,你出个部分分不给部分分数据你是人吗?是人吗? /fn
@斜揽残箫
为啥会有伞兵模拟赛第一题出大模拟并且不告诉你细节啊 /fn
题面无细节,尤其是 T2。
@KnightL
你家一个菜市场只卖一种菜啊。这哪是阅读理解啊,这就是作文续写啊。
第一次体验到挂分的快乐,T2 因为少了一个 if
丢了 40pts。
T3 大方向对了(不过一直在想怎么 \(\mathcal O(n)\) Check),但没想到题解的约束比我的结论还紧凑,然后直接 \(3^{14}\) Check。还是太菜了。
T1 模拟也有一堆细节没处理好,犯得错误放在代码开头了。
T1 | T2 | T3 |
---|---|---|
T208733 robo | T208734 expand | T208735 birthday |
A robo
大模拟。
注意细节,按照题意模拟即可。(((
这里赛后发现了一个问题,就是洛谷用 getline
读入的时候会读入回车 \n
,而 lemon 不会。
其他的看代码吧。哦吼吼我的 200+,7kb 的代码!
/*
Work by: Suzt_ilymtics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
大模拟,做nm!
1、WG 函数返回错误,应直接返回而非 break。
2、字符串的奇怪问题。
3、洛谷 getline 读回车,lemon 不读
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<string>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl
using namespace std;
const int MAXN = 1e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;
struct Robot {
int x, y, a, b, c;
int atk, fx, soc;
}R;
int n, m, K;
string s;
int stc[MAXN], sc = 0; // 弹夹。
int Map[222][222], blood[222][222];
bool flag = false, Flag = false;
int read(){
int s = 0, f = 0;
char ch = getchar();
while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
return f ? -s : s;
}
void Clear() {
R.x = R.y = R.a = R.b = R.c = R.atk = R.fx = R.soc = 0;
n = 0, m = 0, K = 0; s.clear();
sc = 0, Flag = false, flag = false;
memset(stc, false, sizeof stc);
memset(Map, false, sizeof Map);
memset(blood, false, sizeof blood);
}
void Get_Map() {
n = read(), m = read();
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= m; ++j) {
Map[i][j] = read();
if(Map[i][j] == 2) blood[i][j] = 2;
}
}
}
void Get_Robot() { // 初始化机器人信息
R.x = read() + 1, R.y = read() + 1, R.a = read(), R.b = read(), R.c = read(), K = read();
R.atk = 0, R.fx = 0, R.soc = 0, sc = 0; // 初始炮口朝向和机器人朝向为 0 (0,1,2,3 分别表示上左下右),弹夹初始容量为 0
}
void Turn_ATK() { // FT x
int x = s[3] - '0';
if(s[3] < '0' || s[3] > '9') return (void)(flag = true);
if(s[4] == '.') return (void)(flag = true);
if(s[4] >= '0' && s[4] <= '9') return (void)(flag = true);
if(x != 0 && x != 1) return (void)(flag = true);
if(x == 0) R.atk = (R.atk + 1) % 4;
else R.atk = (R.atk + 3) % 4;
}
void Push_ATK() { // FF i
int x = s[3] - '0';
if(s[3] < '0' || s[3] > '9') return (void)(flag = true);
if(s[4] == '.') return (void)(flag = true);
if(s[4] >= '0' && s[4] <= '9') return (void)(flag = true);
if(x != 0 && x != 1) return (void)(flag = true);
if(x == 0) {
if(R.c == 0) return ;
if(sc == R.a) return (void)(flag = true);
R.c --, stc[++sc] = 1;
} else {
if(R.b == 0) return ;
if(sc == R.a) return (void)(flag = true);
R.b --, stc[++sc] = 2;
}
}
void Attack() { // FE
int len = s.length();
if(s.length() > 3) return (void)(flag = true);
if(sc == 0) return ;
int x = stc[sc--];
if(R.atk == 0) {
for(int i = R.x - 1; i; --i) {
if(Map[i][R.y] == 1) break;
if(blood[i][R.y] > 0) {
blood[i][R.y] -= x;
if(blood[i][R.y] <= 0) Map[i][R.y] = 0, R.soc ++;
break;
}
}
} else if(R.atk == 1) {
for(int j = R.y - 1; j; --j) {
if(Map[R.x][j] == 1) break;
if(blood[R.x][j] > 0) {
blood[R.x][j] -= x;
if(blood[R.x][j] <= 0) Map[R.x][j] = 0, R.soc ++;
break;
}
}
} else if(R.atk == 2) {
for(int i = R.x + 1; i <= n; ++i) {
if(Map[i][R.y] == 1) break;
if(blood[i][R.y] > 0) {
blood[i][R.y] -= x;
if(blood[i][R.y] <= 0) Map[i][R.y] = 0, R.soc ++;
break;
}
}
} else if(R.atk == 3) {
for(int j = R.y + 1; j <= m; ++j) {
if(Map[R.x][j] == 1) break;
if(blood[R.x][j] > 0) {
blood[R.x][j] -= x;
if(blood[R.x][j] <= 0) Map[R.x][j] = 0, R.soc ++;
break;
}
}
}
}
void Turn_itself() { // WT x
int x = s[3] - '0';
if(s[3] < '0' || s[3] > '9') return (void)(flag = true);
if(s[4] == '.') return (void)(flag = true);
if(s[4] >= '0' && s[4] <= '9') return (void)(flag = true);
if(x != 0 && x != 1) return (void)(flag = true);
if(x == 0) R.fx = (R.fx + 1) % 4;
else R.fx = (R.fx + 3) % 4;
}
void Walk() { // WG y
if(s[4] == '.') return (void)(flag = true);
int x = 0;
int len = s.length() - 1;
for(int i = 3; i < len; ++i) {
if(s[i] >= '0' && s[i] <= '9') x = x * 10 + s[i] - '0';
else return (void)(flag = true);
}
if(R.fx == 0) {
int now = R.x;
while(x) {
if(now - 1 < 1 || Map[now - 1][R.y] != 0) return (void)(flag = true);
now --, x--;
}
R.x = now;
} else if(R.fx == 1) {
int now = R.y;
while(x) {
if(now - 1 < 1 || Map[R.x][now - 1] != 0) return (void)(flag = true);
now --, x--;
}
R.y = now;
} else if(R.fx == 2) {
int now = R.x;
while(x) {
if(now + 1 > n || Map[now + 1][R.y] != 0) return (void)(flag = true);
now ++, x--;
}
R.x = now;
} else if(R.fx == 3) {
int now = R.y;
while(x) {
if(now + 1 > m || Map[R.x][now + 1] != 0) return (void)(flag = true);
now ++, x--;
}
R.y = now;
}
}
void Start_Play() {
Get_Map();
Get_Robot();
getline(cin, s);
// char ch = getchar();
for(int i = 1; i <= K; ++i) {
// s.clear();
getline(cin, s);
// char ch = getchar();
// while(ch != '\n') s += ch, ch = getchar();
// cout<<s<<"\n";
// cout<<"flag "<<flag<<" "<<R.x<<" "<<R.y<<"\n";
if(Flag || flag) continue;
if(s[0] == 'F' && s[1] == 'T') Turn_ATK();
else if(s[0] == 'F' && s[1] == 'F') Push_ATK();
else if(s[0] == 'F' && s[1] == 'E') Attack();
else if(s[0] == 'W' && s[1] == 'T') Turn_itself();
else if(s[0] == 'W' && s[1] == 'G') Walk();
else if(s[0] == 'E' && s[1] == 'N' && s[2] == 'D') Flag = true;
else flag = true;
}
if(!Flag) flag = true;
if(flag) puts("ERROR");
else puts("Complete");
printf("%d %d\n", R.x - 1, R.y - 1);
printf("%d\n", R.soc);
printf("%d %d %d %d\n", R.atk, R.fx, R.b, R.c);
}
int main()
{
// freopen("robo.in","r",stdin);
// freopen("robo.out","w",stdout);
int T = read();
while(T--) Clear(), Start_Play();
return 0;
}
/*
1
5 5
2 0 0 0 0
0 0 0 0 0
0 0 1 0 0
0 0 0 0 0
0 0 0 0 0
4 4 3 1 1 7
WG 3
WT 0
WG 4
FF 1
FE
END
END
*/
B expand
首先预处理一个二维前缀和。
然后在枚举每一个位置枚举每一个 \(s\) 处理出在某个位置的体格。
然后枚举每一个要到的点(包括起点),直接 bfs 跑最短路,预处理出到其他点的最短距离及最大体格之和。
然后你考虑状压 DP。
设 \(f[S][i]\) 表示已经到过的位置状态为 \(S\),最后一个到的点为 \(i\) 的最大体格之和。
设下一个状态为 \(T = (S \mid (1 << j - 1))\)。
转移方程为:
同时记录一个最大体格之和:
其中 \(dis[i][j], w[i][j]\) 分别表示第 \(i\) 个菜店到第 \(j\) 个菜店的距离和最大体格之和。
最后统计一下最优解即可。
时间复杂度为 \(\mathcal O(nm + nms + pnm + 2^p p^2)\)。
/*
Work by: Suzt_ilymtics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl
using namespace std;
const int MAXN = 2e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;
int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};
struct node {
int x, y;
}b[18];
int n, m, s, p, sc = 0;
int a[330][330];
int dis[18][18], w[18][18], val[330][330];
int Dis[330][330], Val[330][330];
int f[MAXN][18], g[MAXN][18];
bool vis[330][330], flag[330][330];
int read(){
int s = 0, f = 0;
char ch = getchar();
while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
return f ? -s : s;
}
int Calc(int sx, int sy, int ex, int ey) { return a[ex][ey] + a[sx - 1][sy - 1] - a[sx - 1][ey] - a[ex][sy - 1]; }
bool Check(node v) { return v.x < 1 || v.y < 1 || v.x > n || v.y > m; }
void bfs(int sx, int sy) {
memset(Dis, 0x3f, sizeof Dis);
memset(Val, 0, sizeof Val);
memset(vis, false, sizeof vis);
queue<node> q;
q.push((node){sx, sy});
Dis[sx][sy] = 0, vis[sx][sy] = true;
while(!q.empty()) {
node u = q.front(); q.pop();
vis[u.x][u.y] = false;
for(int i = 0; i < 4; ++i) {
node v = (node){u.x + dx[i], u.y + dy[i]};
if(Check(v) || flag[v.x][v.y]) continue;
if(Dis[v.x][v.y] > Dis[u.x][u.y] + 1) {
Dis[v.x][v.y] = Dis[u.x][u.y] + 1;
Val[v.x][v.y] = Val[u.x][u.y] + val[v.x][v.y];
if(!vis[v.x][v.y]) q.push(v), vis[v.x][v.y] = true;
} else if(Dis[v.x][v.y] == Dis[u.x][u.y] + 1) {
Val[v.x][v.y] = max(Val[v.x][v.y], Val[u.x][u.y] + val[v.x][v.y]);
if(!vis[v.x][v.y]) q.push(v), vis[v.x][v.y] = true;
}
}
}
}
int main()
{
// freopen("expand2.in","r",stdin);
// freopen("expand.out","w",stdout);
n = read(), m = read(), s = read();
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
a[i][j] = a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1] + (flag[i][j] = read());
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= m; ++j) {
for(int k = 0; k <= s; ++k) {
int sx = i - k, sy = j - k, ex = i + k, ey = j + k;
if(sx < 1 || sy < 1 || ex > n || ey > m) break;
int sum = Calc(sx, sy, ex, ey);
if(sum) break;
else val[i][j] = k;
}
}
}
b[++sc].x = read() + 1, b[sc].y = read() + 1, p = read();
for(int i = 1; i <= p; ++i) b[++sc].x = read() + 1, b[sc].y = read() + 1;
for(int i = 1; i <= sc; ++i) {
bfs(b[i].x, b[i].y);
for(int j = 1; j <= sc; ++j) {
dis[i][j] = Dis[b[j].x][b[j].y];
w[i][j] = Val[b[j].x][b[j].y];
}
}
memset(f, 0x3f, sizeof f);
f[1][1] = 0, g[1][1] = val[b[1].x][b[1].y];
for(int S = 0; S < (1 << sc); ++S) {
for(int i = 1; i <= sc; ++i) {
if(!(S & (1 << i - 1))) continue;
for(int j = 1; j <= sc; ++j) {
if(S & (1 << j - 1)) continue;
int T = (S | (1 << j - 1));
if(f[T][j] > f[S][i] + dis[i][j]) {
f[T][j] = f[S][i] + dis[i][j];
g[T][j] = g[S][i] + w[i][j];
} else if(f[T][j] == f[S][i] + dis[i][j]) {
g[T][j] = max(g[T][j], g[S][i] + w[i][j]);
}
}
}
}
int ans = 0x3f3f3f3f, res = 0, S = (1 << sc) - 1;
for(int j = 1; j <= sc; ++j) {
if(ans > f[S][j]) ans = f[S][j], res = g[S][j];
else if(ans == f[S][j]) res = max(res, g[S][j]);
}
printf("%d %d\n", ans, res);
return 0;
}
C birthday
前 \(10\%\) 部分分,啥都不用干,直接 return 0
。
对于 \(r-l+1\) 的部分分,对于每个操作 \(1\) 可以 \(3^n\) 枚举所有情况。爆搜位运算均可。对于操作 \(2\) 可以暴力修改。
但是这个出题人 tmd 没放这个部分分的数据啊!/fn
考虑正解。
设 \(len = r - l + 1\),可组成的值域范围为 \([len, len \times V]\)。
然后 \(len\) 个数可组成的数的数量为 \(2^{len}\),
所以当 \(2^{len} > len \times V\) 的时候就一定有解。
解的 \(len\) 的最小整数解为 \(14\),
所以当 \(len \ge 14\) 是一定有解。
考虑 \(len < 13\) 的情况怎么做,直接爆搜 \(3^{len}\) 是承受不了的。
但是这里有一句很著名的话:
Stop learning useless algorithms, go and solve some problems, learn how to use binary search.
所以分开搜就可以了。在搜后半部分的时候也要判断能不能和前半部分匹配,时间复杂度还是 \(3^7\) 级别,可以通过。
/*
Work by: Suzt_ilymtics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl
using namespace std;
const int MAXN = 1e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;
int n, m, V, opt, l, r, mid;
int a[MAXN], stc[MAXN], sc = 0;
int f[MAXN][22];
bool flag[MAXN], Flag = false;
int read(){
int s = 0, f = 0;
char ch = getchar();
while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
return f ? -s : s;
}
namespace Seg {
#define lson i << 1
#define rson i << 1 | 1
int lazy[MAXN << 2];
void Push_up(int i) { ; }
void Push_down(int i) {
if(lazy[i] == 0) return ;
lazy[lson] = lazy[i] + lazy[lson];
lazy[rson] = lazy[i] + lazy[rson];
lazy[i] = 0;
}
void Modify(int i, int l, int r, int L, int R) {
if(L <= l && r <= R) return (void)(lazy[i] = lazy[i] + 1);
Push_down(i);
int mid = (l + r) >> 1;
if(mid >= L) Modify(lson, l, mid, L, R);
if(mid < R) Modify(rson, mid + 1, r, L, R);
Push_up(i);
}
void Query(int i, int l, int r, int L, int R) {
if(l == r) {
for(int j = 0; j <= 20; ++j) if(lazy[i] & (1 << j)) a[l] = f[a[l]][j];
return (void)(lazy[i] = 0);
}
Push_down(i);
int mid = (l + r) >> 1;
if(mid >= L) Query(lson, l, mid, L, R);
if(mid < R) Query(rson, mid + 1, r, L, R);
}
}
void Init() {
for(int i = 0; i < V; ++i) f[i][0] = i * i * i % V;
for(int i = 1; i <= 20; ++i)
for(int j = 0; j < V; ++j)
f[j][i] = f[f[j][i - 1]][i - 1];
}
void dfsl(int pos, int dis, bool k) {
if(Flag) return ;
if(pos == mid + 1) {
if(!k) return ;
if(!dis) Flag = true;
else if(dis >= 0 && !flag[dis]) flag[dis] = true, stc[++sc] = dis;
return ;
}
dfsl(pos + 1, dis, k);
dfsl(pos + 1, dis + a[pos] + 1, true);
dfsl(pos + 1, dis - a[pos] - 1, true);
}
void dfsr(int pos, int dis, bool k) {
if(Flag) return ;
if(pos == r + 1) {
if(!k) return ;
if(!dis) Flag = true;
else if(dis >= 0 && flag[dis]) Flag = true;
return ;
}
dfsr(pos + 1, dis, k);
dfsr(pos + 1, dis + a[pos] + 1, true);
dfsr(pos + 1, dis - a[pos] - 1, true);
}
int main()
{
n = read(), m = read(), V = read();
Init();
for(int i = 1; i <= n; ++i) a[i] = read();
for(int i = 1; i <= m; ++i) {
opt = read(), l = read(), r = read();
if(opt == 2) {
Seg::Modify(1, 1, n, l, r);
} else {
if(r - l + 1 >= 14) { puts("Yes"); continue; }
Seg::Query(1, 1, n, l, r);
mid = (l + r) >> 1;
sc = 0, Flag = false;
dfsl(l, 0, false), dfsr(mid + 1, 0, false);
while(sc) flag[stc[sc--]] = false;
Flag ? puts("Yes") : puts("No");
}
}
return 0;
}
/*
20 20 152
3 26 133 54 79 81 72 109 66 91 82 100 35 23 104 17 51 114 12 58
2 1 17
2 6 12
1 1 12
2 3 5
2 11 11
2 7 19
2 6 15
1 5 12
1 1 9
1 10 19
2 3 19
2 6 20
2 1 13
2 1 15
2 1 9
1 1 1
2 1 7
2 7 19
2 6 19
2 3 6
*/