A
问:
数比大小
解:
先把数变成 个位数+n个0
注意小数失精
B
问:
给一串数,让选若干对数字a和b,俩数都出现过,而且a%b没有出现,数可以重复选
解:
因为数可以重复,选最小的为b,任意一个数mod b一定都没出现
C
问:
屠龙游戏,给龙的血量和刀的属性,和攻击的次数,每次攻击龙会持续k时间掉血,攻击后属性重置。
每次攻击间隔相减判断和k的大小即可,注意最后一次一定是掉了k血。
求最小的k。
解:
因为越大一定越容易成功,考虑二分答案,O(n)check,总复杂度为O(nlogn)
D
问:
MEX(x1…xk)是 x1~xk中未出现的最小非负整数
如果一个序列被认为是MEX-CORRECT 的 那么
|xi-MEX(x1…xi)|<=1
(如果 i=1 就满足 |x1-MEX(x1)|<=1,如果i=2 要满足|x1-MEX(x1)|<=1和 |x2-MEX(x1,x2)|<=1)
现在 给出n个数 问一共有多少 MEX-CORRECT的序列
思:
显然是个DP的题,看眼数据,O(n)的dp,
先看题意,模拟一下发现只有两种数列。
①单调递增的
0 1 1 2 3 3 3 4
②中间调了一个数
0 1 2 2 3 3 3 5 3 5 5 5 3 3 5 3
不难发现,这种数列,前面是个①数列,直到加入了一个比最后一个数Xn大2的数,然后这个数列就只能选Xn或者Xn+2了
dp[i][0]表示结尾为i的第一种数列的所有方案数。
dp[x][1]表示最大值为x的第二种数列的所有方案数(已经存在x和x-2了)。
考虑如何转移 和 初始状态
转移:
首先明确,数列是有顺序的 我们按照1->n处理的时候,循环到i刚开始的时候,dp存的就是1-> i-1 的所有可行方案数,当前位置可以直接根据dp的值进行转移。
如果读到了一个数x
组成第一种数列:
他可以放在结尾是x的第一种数列和结尾为x-1的第一种数列
状态转移方程:
之前结尾就是x的数列现在结尾又多了个x,可以选或者不选,如果不选,还是之前那些方案,如果选了,相当于多了之前那些方案。
现在结尾未x的方案数还多了之前结尾为x-1的方案数。
有 dp[x][0] += dp[x][0] + dp[x-1][0]
组成第二种数列:
他可以放在最大值为x和x+2的第二种数列的结尾
他还可以放在结尾为x-2的第一种数列的结尾
状态转移方程:
放在最大值为x的数列结尾,多了一种选择
dp[x][1] += dp[x][1]
放在最大值为x+2的数列的结尾,也多了一种选择
dp[x+2][1] += dp[x+2][1]
放在结尾为x-2的第一种数列结尾
最大值为x第二种数列多了结尾为x-2的第一种数列的方案数
dp[x][1] += dp[x-2][0];
初始状态:
我们考虑什么都不选的时候,假设数列最开始有一个-1,方案数为1,只有一个-1就是可行数列是空的时候的情况。
第一个数是1或者0,如果是1,就相当于-1和1隔了两个数,从第二个情况转移过去,如果第一个数是0,也是从-1这个状态转移过去,所以默认一开始有个-1。
统计答案,即结尾为原数列存在的各个数的第一种序列的所有情况,最大数为 原数列存在的各个数的第二种序列的所有情况。
其实1-n不存在的也是0,直接全部都加上即可
注意:所有数+2,防止越界。
#include <bits/stdc++.h>
using namespace std;
//#pragma GCC optimize (2)
//#pragma GCC optimize (3)
#pragma GCC optimize (Ofast)
#define fastio ios_base::sync_with_stdio(0); cin.tie(NULL);
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define LL long long
#define endl "\n"
#define debug1 cout << "???" << endl;
#define debug2(x) cout << #x << ": " << x << endl;
const int INF = 0x3f3f3f3f;
const int N = 5e5+7;
const int MOD = 998244353;
LL T, n, a[N], vis[N], ans, dp[N][2];
int main()
{
fastio
cin >> T;
while(T--)
{
cin >> n;
rep(i, 0, n+2)
dp[i][0] = dp[i][1] = 0;
ans = 0;
dp[1][0] = 1;
rep(i, 1, n)
{
cin >> a[i];
a[i] += 2;
dp[a[i]][0] += dp[a[i]][0] + dp[a[i]-1][0];
dp[a[i]][1] += dp[a[i]][1] + dp[a[i]-2][0];
dp[a[i] + 2][1] += dp[a[i] + 2][1] ;
dp[a[i]][0] %= MOD;
dp[a[i]][1] %= MOD;
dp[a[i] + 2][1] %= MOD;
}
rep(i, 2, n+2)
{
ans += dp[i][0];
ans %= MOD;
ans += dp[i][1];
ans %= MOD;
}
cout << ans << endl;
}
return 0;
}
E
问:
给一张地图,有一个终点,判断机器人能否从任意一个点走到终点,机器人每次可以收到一个指令,他一定不会走指令的方向,如果有别的方向可以走,他就会随机走一个,求所有可以到终点的点。
解:
考虑每一个点:
如果当前点可以走到终点,需要满足,这个点有k个方向可以走,至少k-1个方向是一定可以保证能通向终点的,那么我们只需要让机器人不走不通的方向即可。
要判断这个点能否走,需要已经知道他另外几个方向能否走,所以我们维护一个可以走的状态,从这些状态向没有走的地方拓展,然后判断是不是在答案里。
实际上就是拓扑排序。
如何维护这种可以走的状态呢?肯定是从终点开始才能开始得到那些点是可以走的。需要用一个bfs从终点开始维护所有一定可以走的路径。
预处理每个点可以走的方向。
把终点丢到BFS队列里,然后队列里都是一定可以走到的,队列每个点拓展的时候,拓展到的点直接度数减一,如果度数小于等于一就说明这个点也可以走,丢到队列里即可。
注意:
不能用cout << endl 1e5个endl就得跑两秒,用\n。
因为不能直接开 1e6 x 1e6大的数组,需要开N个vector,每次初始化resize一下,最后记得clear,就可以当二维数组用了
#pragma GCC optimize (2)
#pragma GCC optimize (3)
#pragma GCC optimize (Ofast)
#define fastio ios_base::sync_with_stdio(0); cin.tie(NULL);
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define LL long long
#define debug1 cout << "???" << endl;
#define debug2(x) cout << #x << ": " << x << endl;
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 1e6+7;
int T, n, m, lx, ly,dir[2][4] = {-1, 0, 1, 0, 0, 1, 0, -1};
char ch;
vector <int> a[N], vis[N], w[N], pre[N];
int DFS(int x, int y);
void Solve()
{
//vis 1 一定可以到终点 a 1可以走
cin >> n >> m;
rep(i, 0, n+1)
{
w[i].resize(m+2);
a[i].resize(m+2);
vis[i].resize(m+2);
pre[i].resize(m+2);
}
rep(i, 1, n)
{
rep(j, 1, m)
{
cin >> ch;
if(ch == '.')
a[i][j] = 1;
else if(ch == '#')
a[i][j] = 0;
else if(ch == 'L')
{
a[i][j] = 1;
vis[i][j] = 1;
lx = i, ly = j;
}
}
}
//求每个点可以走的方向数
rep(i, 1, n)
{
rep(j, 1, m)
{
if(!a[i][j]) continue;
rep(k, 0, 3)
{
int tx = i + dir[0][k], ty = j + dir[1][k];
if(a[tx][ty])
w[i][j]++;
}
}
}
queue <pair<int, int>> q;
q.push(make_pair(lx, ly));
a[lx][ly] = 2;
while(!q.empty())
{
int x = q.front().first, y = q.front().second; q.pop();
pre[x][y] = 1;
rep(i, 0, 3)
{
int tx = x + dir[0][i], ty = y + dir[1][i];
if(!a[tx][ty]) continue;
if(--w[tx][ty] <= 1)
{
vis[tx][ty] = 1;
if(!pre[tx][ty])
q.push(make_pair(tx, ty));
}
}
}
rep(i, 1, n)
{
rep(j, 1, m)
{
if(a[i][j] == 1)
{
if(vis[i][j] == 1)
cout << '+';
else
cout << '.';
}
else if(a[i][j] == 2)
cout << 'L';
else
cout << '#';
}
cout << '\n';
}
//每次结束后清空
rep(i, 0, n+1)
{
a[i].clear();
vis[i].clear();
w[i].clear();
pre[i].clear();
}
}
int main()
{
fastio
cin >> T;
while(T--)
Solve();
return 0;
}