2022.3.9
蓝书
AcWing 106. 动态中位数
思路:对顶堆,设第一个数为mid,之后的数读进来一次比较大小决定放入大根堆还是小根堆,然后当i为奇数的时候输出当前的中位数。
注意一下格式问题,当中位数的个数不能整除10的时候最后还要加个换行。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
const int N=1e5+10,INF=1e8;
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
int n, m,cnt=1;
scanf("%d%d", &n, &m);
printf("%d %d\n", n, m / 2 + 1);
priority_queue<int> mmax;
priority_queue<int, vector<int>, greater<int> > mmin;
int x,mid;
scanf("%d", &x);
printf("%d ", x);
mid = x;
for (int i = 2; i <= m;i++)
{
scanf("%d", &x);
if(x>mid)
mmin.push(x);
else
mmax.push(x);
if(i%2==1)
{
while(mmax.size()!=mmin.size())
{
if(mmax.size()>mmin.size())
{
mmin.push(mid);
mid = mmax.top();
mmax.pop();
}
else if(mmax.size()<mmin.size())
{
mmax.push(mid);
mid = mmin.top();
mmin.pop();
}
}
printf("%d ", mid);
cnt++;
if(cnt%10==0)
printf("\n");
}
}
if(cnt%10!=0)
printf("\n");
}
return 0;
}
AcWing 107. 超快速排序
思路:归并排序求逆序对
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=5e5+10,INF=1e8;
int a[N],tmp[N];
ll cnt;
void mergesort(int l,int r)
{
if(l>=r)
return;
int mid = l + r >> 1;
mergesort(l,mid);
mergesort(mid + 1, r);
int i = l, j = mid + 1,k=0;
while(i<=mid&&j<=r)
{
if(a[i]<=a[j])
tmp[k++] = a[i++];
else
{
tmp[k++] = a[j++];
cnt += mid - i + 1;
}
}
while(i<=mid)
tmp[k++] = a[i++];
while(j<=r)
tmp[k++] = a[j++];
for (int i = l, j = 0; i <= r;i++,j++)
a[i] = tmp[j];
}
int main()
{
int n;
while(scanf("%d",&n)&&n)
{
cnt = 0;
for (int i = 1; i <= n;i++)
scanf("%d", &a[i]);
mergesort(1, n);
printf("%lld\n", cnt);
}
return 0;
}
AcWing 108. 奇数码问题
两个局面可达,当且仅当两个局面的逆序对的积偶性相同。当空格左右移动的时候是不会改变原本序列的顺序的,只要当上下移动的时候才会改变,也就是与前或后的n-1个数位置互换,因为n为奇数,所以n-1即逆序对个数的变化只能为偶数
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=250010,INF=1e8;
int a[N],b[N],tmp[N];
ll cnt;
void mergesorta(int l,int r)
{
if(l>=r)
return;
int mid = l + r >> 1;
mergesorta(l,mid);
mergesorta(mid + 1, r);
int i = l, j = mid + 1,k=0;
while(i<=mid&&j<=r)
{
if(a[i]<=a[j])
tmp[k++] = a[i++];
else
{
tmp[k++] = a[j++];
cnt += mid - i + 1;
}
}
while(i<=mid)
tmp[k++] = a[i++];
while(j<=r)
tmp[k++] = a[j++];
for (int i = l, j = 0; i <= r;i++,j++)
a[i] = tmp[j];
}
void mergesortb(int l,int r)
{
if(l>=r)
return;
int mid = l + r >> 1;
mergesortb(l,mid);
mergesortb(mid + 1, r);
int i = l, j = mid + 1,k=0;
while(i<=mid&&j<=r)
{
if(b[i]<=b[j])
tmp[k++] = b[i++];
else
{
tmp[k++] = b[j++];
cnt += mid - i + 1;
}
}
while(i<=mid)
tmp[k++] = b[i++];
while(j<=r)
tmp[k++] = b[j++];
for (int i = l, j = 0; i <= r;i++,j++)
b[i] = tmp[j];
}
int main()
{
int n;
while(~scanf("%d",&n))
{
cnt = 0;
int num = 1;
for (int i = 1; i <= n*n;i++)
{
int x;
scanf("%d", &x);
if(x)
a[num++] = x;
}
num = 1;
for (int i = 1; i <= n*n;i++)
{
int x;
scanf("%d", &x);
if(x)
b[num++] = x;
}
mergesorta(1, num);
ll ans = cnt;
cnt = 0;
mergesortb(1, num);
if((ans&1)==(cnt&1))
printf("TAK\n");
else
printf("NIE\n");
}
return 0;
}
AcWing 177. 噩梦
按照蓝书的思路来实现就行,先拓展鬼的占领范围,因为男孩和女孩都可以移动所以要有2个bfs,我们判断能遇见的因素是当bfs女孩的时候枚举女孩走的点,如果男孩走过当前点那说明他们就可以在最短的时间相遇了。直接返回即可。
有几个小坑点:每次都要先更新一下鬼的和人的曼哈顿距离,枚举3次男孩的步数的时候别忘了枚举的点的个数不能超过队列里的点,女孩同理。还有就是枚举女孩的点的时候不能把判断的visb放在for循环的外面,因为这样就不能保证女孩是第一次符合visb了,稍微理解一下。一开始甚至sb的把dis数组也加上了,后面发现根本没必要,然后就是写着写着容易忘记更新vis,得时刻警记。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N=800+10,INF=1e8;
char mp[N][N];
int dx[4] = {1, -1, 0, 0}, dy[4] = {0, 0, 1, -1};
int n,m,tt,g1x,g1y,g2x,g2y;
int visb[N][N], visg[N][N];
queue <PII> boy,girl;
bool check(int x,int y)
{
return (x >= 1 && x <= n && y >= 1 && y <= m && mp[x][y] != 'X' && (abs(x - g1x) + abs(y - g1y) > 2 * tt )&& (abs(x - g2x) + abs(y - g2y) > 2 * tt));
}
int bfs()
{
tt = 0;
memset(visb, 0, sizeof visb);
memset(visg, 0, sizeof visg);
while(boy.size()||girl.size())
{
tt++;
for (int i = 1; i <= 3;i++)
{
int bcnt = boy.size();
for (int k = 1; k <= bcnt;k++)
{
auto b = boy.front();
boy.pop();
int x = b.first, y = b.second;
if(visb[x][y]&&visg[x][y])
return tt;
if(!check(x,y))
continue;
visb[x][y] = 1;
for (int j = 0; j < 4;j++)
{
int xx = x + dx[j], yy = y + dy[j];
if(check(xx,yy)&&!visb[xx][yy])
{
visb[xx][yy] = 1;
boy.push({xx, yy});
}
}
}
}
int gcnt = girl.size();
for (int i = 1; i <= gcnt;i++)
{
auto g = girl.front();
girl.pop();
int x = g.first, y = g.second;
// if(visb[x][y]&&visg[x][y])这样是错的
// return tt;
if(!check(x,y))
continue;
visg[x][y] = 1;
for (int i = 0; i < 4;i++)
{
int xx = x + dx[i], yy = y + dy[i];
if(check(xx,yy)&&!visg[xx][yy])
{
if(visb[xx][yy])
return tt;
girl.push({xx, yy});
visg[xx][yy] = 1;
}
}
}
}
return -1;
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
int cnt=0;
while(boy.size())
boy.pop();
while(girl.size())
girl.pop();
scanf("%d%d", &n, &m);
for (int i = 1; i <= n;i++)
{
for (int j = 1; j <= m;j++)
{
cin >> mp[i][j];
if(mp[i][j]=='M')
{
boy.push({i, j});
visb[i][j] = 1;
}
else if(mp[i][j]=='G')
{
girl.push({i, j});
visg[i][j] = 1;
}
else if(mp[i][j]=='Z')
{
if(cnt==1)
{
g2x = i, g2y = j;
}
else
g1x = i, g1y = j;
cnt++;
}
}
}
int ans=bfs();
printf("%d\n", ans);
}
return 0;
}
Codeforces Round #776 (Div. 3)
A. Deletions of Two Adjacent Letters
题意:给你一个长度为奇数字符串s和一个字母c,你可以通过删除相邻字母来减少字符串的长度,问最后字符串是否有可能变成字母c
思路:对于字符串找一下是否存在一个奇数位该位为字母c。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=1e5+10,INF=1e8;
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
int n,f=0;
scanf("%d", &n);
char s[55]={0},x;
scanf("%s", s+1);
cin >> x;
int len = strlen(s + 1);
for (int i = 1; i <= len;i++)
{
if(s[i] == x&&(i%2==1))
{
f = 1;
break;
}
}
if(f)
printf("YES\n");
else
printf("NO\n");
}
return 0;
}
B. DIV + MOD
题意:在给定的区间[l,r]里找一个数满足这个数整除x和模x的和最大
贪心,首先这个值最小也得是r/x+r%x,但为了让模的数更大我们需要找接近x的数如:x-1,x-2...如此就有模的最大值,x-1%x。于是我们需要在区间找这样一个值x=r/a*a-1。如果r<a的话那么这个值x就是-1,如果r>a的话,如果a的某个整数倍-1,最后再和最小值的答案比较一下。
有点迷糊,看了红黑的代码稍微理解了一点
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=1e5+10,INF=1e8;
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
int l, r, x;
scanf("%d%d%d", &l, &r, &x);
int ans = r / x + r % x;
int res = r / x * x - 1;
if(res>=l)
ans = max(ans, res / x + res % x);
printf("%d\n", ans);
}
return 0;
}
C.Weight of the System of Nested Segments
题意:给你一个x轴,上面m个点,点有坐标和权值,你需要找到n个区间满足区间是从大到小包含的,从外到内一层套一层。
思路:先排序一下点的权值,然后选2*n个点组成n个区间,然后排序一下他们的x轴坐标,最后按照各自最开始的位置输出。
甚至比B好想一点
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=2e5+10,INF=1e8;
struct node
{
ll id,x,w;
}a[N];
bool cmp(node a,node b)
{
return a.w<b.w;
}
bool cmp1(node a,node b)
{
return a.x<b.x;
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++)
{
scanf("%lld%lld", &a[i].x, &a[i].w);
a[i].id = i;
}
ll sum=0;
sort(a+1,a+1+m,cmp);
sort(a+1,a+1+2*n,cmp1);
for(int i=1;i<=2*n;i++)
sum += a[i].w;
printf("%lld\n", sum);
for(int i=1;i<=n;i++)
printf("%lld %lld\n", a[i].id, a[2 * n - i + 1].id);
}
return 0;
}