比赛链接
A
暴力模拟就好了
范围小,直接枚举每一秒,注意坑定在如果A已经超过B距离t了,那么在等待的s秒内不能继续更新为s秒,也就是说,在等待时不要进行距离判定,我这里是用了一个标记变量f来标记是否已经开始等待了
void solve(){
scanf("%d %d %d %d %d",&v1,&v2,&t,&s,&l);
int res = 0,p1 = 0, p2 = 0,time=0,f = 0;
while(1) {
time ++;
if(res) {
res --;
} else {
f = 0;
p1 += v1;
}
p2 += v2;
if(p1 - p2 >= t) {
if(!f){
f = 1;
res = s;
}
}
if(p1 >= l && p2 >= l) {
printf("Tie %d\n",time);
return ;
} else if(p2 >= l) {
printf("Hong %d\n",time);
return ;
} else if(p1 >= l) {
printf("Ming %d\n",time);
return;
}
}
}
B
题目不难,只是需要处理很多细节,
我自己的写法是 对于每个数值都加上1e6 然后开一个2e6的vector数组,读入的时候,对于每一种值,都存入对应的下标
要求绝对值最小的话,那么就遍历数组,对于每一个元素a[i],我们就二分查找-a[i]附近的数,因为涉及到不存在的情况,所以我是对于每个数都二分三次,分别找出小于-a[i]中最大的值,大于-a[i]的最小的值,以及-a[i],因为我们在读入的时候,储存了每种数值共有那些下标,所以我们直接在三种大小内从小打到找出第一个下标不等于i的下标,然后更新最小值就可以了
vector<int> v[maxm];
struct node {
int val, pos;
}arr[maxn];
int n;
bool cmp(node a,node b) {
if(a.val == b.val) {
return a.pos < b.pos;
} else {
return a.val < b.val;
}
}
int bin1(int val) {
int L = 1, R = n, pos = -1;
while(L <= R) {
int mid = L + R >> 1;
if(arr[mid].val > val) {
pos = mid;
R = mid - 1;
} else {
L = mid + 1;
}
}
return pos;
}
int bin2(int val) {
int L = 1, R = n, pos = -1;
while(L <= R) {
int mid = L + R >> 1;
if(arr[mid].val < val) {
pos = mid;
L = mid + 1;
} else {
R = mid - 1;
}
}
return pos;
}
void solve(){
scanf("%d",&n);
rep(i,1,n) {
scanf("%d",&arr[i].val);
int val = arr[i].val + 1e6;
v[val].pb(i);
arr[i].pos = i;
}
sort(arr + 1, arr + n + 1, cmp);
int res = abs(arr[1].val + arr[2].val) , st = arr[1].pos, ed = arr[2].pos;
rep(i,1,n) {
//de(i)
//de(res)
int tmp = - arr[i].val;
int pos1 = bin1(tmp);
//de(pos1)
int pos2 = bin2(tmp);
// de(pos1)
//de(pos2)
if(pos1 != -1) {
int val = arr[pos1].val;
int sz = v[(int)(val + 1e6)].size();
rep(j,0,sz-1) {
if(v[(int)(val + 1e6)][j] != arr[i].pos) {
int tt = arr[i].val + val;
if(abs(tt) < res) {
res = abs(tt);
st = arr[i].pos;
ed = v[(int)(val + 1e6)][j];
} else if(abs(tt) == res) {
if(arr[i].pos + v[(int)(val + 1e6)][j] < st + ed) {
st = arr[i].pos;
ed = v[(int)(val + 1e6)][j];
}
}
break;
}
}
}
// de(res)
if(pos2 != -1) {
int val = arr[pos2].val;
//de(val)
int sz = v[(int)(val + 1e6)].size();
rep(j,0,sz-1) {
if(v[(int)(val + 1e6)][j] != arr[i].pos) {
int tt = arr[i].val + val;
//de(tt)
if(abs(tt) < res) {
res = abs(tt);
st = arr[i].pos;
ed = v[(int)(val + 1e6)][j];
} else if(abs(tt) == res) {
if(arr[i].pos + v[(int)(val + 1e6)][j] < st + ed) {
st = arr[i].pos;
ed = v[(int)(val + 1e6)][j];
}
}
break;
}
}
}
// de(res)
int vv = tmp + 1e6;
int sz = v[vv].size();
rep(j,0,sz-1) {
if(v[vv][j] != arr[i].pos) {
int tt = arr[i].val + vv;
if(abs(tt) < res) {
res = abs(tt);
st = arr[i].pos;
ed = v[vv][j];
} else if(abs(tt) == res) {
if(arr[i].pos + v[vv][j] < st + ed) {
st = arr[i].pos;
ed = v[vv][j];
}
}
break;
}
}
}
printf("%d %d\n",res,st+ed);
}
C
bfs直接过,搜索入门题…(起点一定不会在高危地区
需要注意的是我们在划高危地区的时候不能把
附近都变成
,因为这样就会把整个地图都蔓延完,我们应该开一个str二维数组存储地图,然后用一个e二维数组,用1代表不可以走,0代表可以走,之后遍历地图,对于每一个高危点,就在e数组内使得这个点所在的九宫格全变为1,之后记录起点终点,结构体储存坐标与步数,然后对于e数组直接跑一个bfs就可以了
char str[100][100];
int n,m,sx,sy,ex,ey;
int nx[4] = {1,0,-1,0};
int ny[4] = {0,-1,0,1};
int e[100][100],vis[100][100];
void change(int x,int y) {
e[x][y] = 1;
rep(i,0,3) {
e[x+nx[i]][y+ny[i]] = 1;
}
e[x-1][y-1]=e[x+1][y+1]=e[x-1][y+1]=e[x+1][y-1]=1;
}
struct node{
int x,y,s;
};
void bfs() {
queue<node>que;
que.push((node){sx,sy,0});
vis[sx][sy] = 1;
while(!que.empty()) {
node now = que.front();
que.pop();
int x = now.x, y = now.y, s = now.s;
if(x == ex && y == ey) {
printf("%d\n",s);
return ;
}
rep(i,0,3) {
int tx = x + nx[i], ty = y +ny[i];
if(tx < 1 || tx > n || ty < 1 || ty > m || e[tx][ty] || vis[tx][ty]) {
continue;
}
vis[tx][ty] = 1;
que.push((node){tx,ty,s+1});
}
}
puts("Impossible");
}
void solve(){
ms(e,0),ms(vis,0);
scanf("%d %d",&n,&m);
rep(i,1,n) {
scanf("%s",str[i] + 1);
}
rep(i,1,n) {
rep(j,1,m) {
if(str[i][j] == '*') {
change(i,j);
}
if(str[i][j] == 'S') {
sx = i;
sy = j;
}
if(str[i][j] == 'E') {
ex = i;
ey = j;
}
}
}
if(e[ex][ey] == 1) {
puts("Impossible");
return ;
}
bfs();
}
中间三个题不会写。。
G
这个简单,因为我们有1到n的所有元素,然后我们可以发现,对于任意n范围的数,我们一定可以变成二进制下每一个数都为1,比如n二进制为 100010,首位一定是1,剩下的每一位都取反 找出的数一定比他小,在他范围内,例如这个就是 011101,异或后就是全为1了,所以我们只需要求出n二进制下共有多少位,然后每一位都变成1就可以了
如果n的二进制本身就全为1,比如1111,那我们只需要用n-1来和1异或就可以了,这样求出来的也是全为1,
注意特判1,1只能和1自己,所以为0
这题似乎不需要开unsigned long long…我开了,然后没注意把int给爆了,,
uLL cal(uLL x) {
uLL res = 0;
while(x) {
x >>= 1;
res ++;
}
return res;
}
void solve(){
uLL n;
cin >> n;
if(n == 1){
cout << 0 << endl;
return ;
}
cout << qpow((uLL)(2),cal(n)) - (uLL)(1) << endl;
}
H
并查集 or BFS ?
我写的并查集,我看有人用bfs也过了,应该都可以吧,
常规并查集,只是多加一个hei(高度)的属性,代表这个集合内元素的总个数,然后对于每个点,如果它周围有和自己不一样的符号的点,就合并(每个点可以重复走),合并的时候注意一下高度的合并就可以了
我们可以用一个pos二维数组,对于每一个点进行位置标记,然后对于每个点,用pos[i][j]就可以代表这个点了
合并高度
int fa = seek(pos[x][y]), fb = seek(pos[tx][ty]);
if(fa != fb) {
dad[fa] = fb;
hei[fb] += hei[fa];
}
全部代码是
int n,m,q;
int pos[maxn][maxn];
char str[maxn][maxn];
int dad[maxn * maxn], hei[maxn * maxn];
int nx[4] = {0,1,0,-1};
int ny[4] = {1,0,-1,0};
int seek(int x) {
return x == dad[x] ? x : dad[x] = seek(dad[x]);
}
void change(int x,int y) {
rep(i,0,3) {
int tx = x + nx[i], ty = y + ny[i];
if(tx < 1 || tx > n || ty < 1 || ty > m) {
continue;
}
if(str[x][y] == str[tx][ty]) {
continue;
}
int fa = seek(pos[x][y]), fb = seek(pos[tx][ty]);
if(fa != fb) {
dad[fa] = fb;
hei[fb] += hei[fa];
}
}
}
void solve(){
scanf("%d %d %d",&n,&m,&q);
rep(i,1,n) {
//rep(j,1,m) {
scanf("%s",str[i] + 1);
//}
}
int tot = 0;
rep(i,1,n) {
rep(j,1,m) {
pos[i][j] = ++ tot;
}
}
rep(i,1,tot) {
dad[i] = i;
hei[i] = 1;
}
rep(i,1,n) {
rep(j,1,m) {
change(i,j);
}
}
while(q --) {
int a,b;
scanf("%d %d",&a,&b);
int f = seek(pos[a][b]);
printf("%d\n",hei[f]);
}
}
I
这个,就是一个欧拉筛,筛出来素数之后再记录一个前缀和,然后直接O1查询了
int prime[maxn];
int sum[maxn];
void ESS(){
int n = 1e7;
for(int i = 0;i <= n;i++){
prime[i] = 1;
sum[i] = 0;
}
int cup[maxn],cnt=0;
for(int i = 2;i <= n;i++){
if(prime[i]){
cup[++ cnt] = i;
}
for(int j = 1;j <= cnt && i*cup[j] <= n; ++ j){
prime[i * cup[j]] = 0;
if(i % cup[j] == 0){
break;
}
}
}
prime[0] = prime[1] = 0;
sum[0] = sum[1] = 0;
rep(i,1,n) {
if(prime[i]) {
sum[i] ++;
}
sum[i] += sum[i-1];
}
}
void solve(){
int a = read(), b = read();
printf("%d\n",sum[b] - sum[a-1]);
}
可恶啊,这个I没抢到一血。。我直接点开这个。。手速过去,然后是第二个写出来的,,就差一点
J
签到题,先比较长度,长度相同遍历找出第一个差异的地方
当然,我是python过的,python大法好
a = int(input())
b = int(input())
if a > b :
print('>')
elif a < b :
print('<')
else :
print('=')
K
组合数学简单题,高中排列组合简单题,会逆元就能写
步数最少,那就是只能向右向下咯,所以我们需要n-1次向下,m-1次向右,所以就是再总的n-1+m-1次走路选择中,找出n-1次来向下走就可以了,所以就是组合数
,so直接打个逆元表就过了
附上逆元解法
取模
就是a乘于b的逆元,b的逆元求法是 inv(b) = mqpow(b,mod-2,mod),所以加一个快速幂就过啦
附上组合数求法
ll fact[maxn];
void init(){
ll n = 2e6;
fact[0] = 1;
for(ll i = 1;i <= n;i++){
fact[i] = (fact[i-1] * i)%mod;
}
}
ll inv(ll x,ll mo){
return mqpow(x,mo-2,mo);
}
ll cal(ll n,ll m,ll mo){
return (fact[n] * inv(fact[m],mo)%mo * inv(fact[n-m],mo)%mo)%mo;
}
void solve(){
ll n,m;
cin >> n >> m;
n--;
m--;
cout << cal(n+m,n,mod) << endl;
}
L
这个其实就是一个考察二分的题目,先sort,然后对于每一个元素作为最小值,二分找出第一个大于a[i]+5的下标,然后减一,起点与终点就是人数,找出最大人数就可以了
LL arr[maxn],n;
void solve(){
scanf("%lld",&n);
rep(i,1,n) {
scanf("%lld",&arr[i]);
}
sort(arr + 1,arr + n + 1);
LL res = 1;
rep(i,1,n) {
LL tmp = arr[i] + 5;
if(tmp > arr[n]) {
LL dis = n - i + 1;
res = max(res, dis);
} else {
int pos = upper_bound(arr + 1, arr + n + 1, tmp) - arr;
pos --;
res = max((LL)(pos - i + 1), res);
}
}
printf("%lld\n",res);
}