2022.3.13
蓝书
AcWing 119. 袭击
思路:平面最近点对,多了个阵营的区别,但还是调了好久,有以下几点需要注意:1,题目数据范围太大如果分治的时候直接sort会超时,得用归并排序。2.因为是浮点数的判断所以得加eps。3.要特判一下当点的类型相同的时候为了不影响最小值的判断,让他们变成当前的最小值就可以。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N = 2e5 + 10;
const double INF = 1e10,eps=1e-6;
double mmin;
struct node
{
double x, y;
int id;
} point[N],tmp[N];
bool cmp(node a, node b) { return a.x < b.x; }
bool cmp2(node a, node b) { return a.y < b.y; }
double dist(node a,node b)
{
if (a.id == b.id)
return mmin;
return (double)sqrt((double)(a.x - b.x) * (a.x - b.x) + (double)(a.y - b.y) * (a.y - b.y));
}
double solve(int l,int r)
{
if(l>=r)
return INF;
int mid = l + r >> 1;
double midx = point[mid].x;
int midid = point[mid].id;
double res = min(solve(l, mid), solve(mid + 1, r));
int k = 0,i=l,j=mid+1;
while(i<=mid&&j<=r)
{
if(point[i].y<point[j].y)
tmp[k++] = point[i++];
else
tmp[k++]=point[j++];
}
while(i<=mid)
tmp[k++] = point[i++];
while(j<=r)
tmp[k++] = point[j++];
for (int i = l, j = 0; i <=r;i++,j++)
point[i] = tmp[j];
k = 0;
for (int i = l; i <= r; i++)
{
// cout << midid << ' ' << point[i].id << '\n';
if (point[i].x >= midx - res && point[i].x <= midx + res)
tmp[k++] = point[i];
}
for (int i = 0; i < k; i++)
{
// cout << midid << ' ' << point[i].id << '\n';
for (int j = i + 1; j < k && tmp[i].y - tmp[j].y+eps <= res; j++)
{
//if (tmp[i].id != tmp[j].id)
// cout << dist(tmp[i], tmp[j]) << endl;
res = min(res, dist(tmp[i], tmp[j]));
}
}
mmin = min(mmin, res);
return res;
}
int main()
{
//ios::sync_with_stdio(false);
//cin.tie(0);
int t;
scanf("%d", &t);
while(t--)
{
int n;
scanf("%d", &n);
for (int i = 0; i < n;i++)
{
scanf("%lf%lf", &point[i].x, &point[i].y);
point[i].id = 1;
}
for (int i = n; i < n*2;i++)
{
scanf("%lf%lf", &point[i].x, &point[i].y);
point[i].id = 2;
}
sort(point, point + 2 * n, cmp);
mmin = dist(point[0], point[2 * n - 1]);
printf("%.3lf\n",solve(0, 2 * n-1));
}
return 0;
}
AcWing 120. 防线
思路:因为防具都在成一排的并且每个位置可能多个防具,那如何找到防具为奇数个的位置,就需要利用到前缀和和二分,把二分和前缀和数组写完发现数据范围太大根本开不了,然后想了一会看了以前交的代码,忘记了一个等差数列性质,我们要求有多少个点也就是等差数列的长度除以公差,也就是一共有多少段,然后段数加1就是点的个数。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=2e5+10,INF=1e8;
int n;
struct node
{
ll s, e, d;
} a[N];
ll check(ll mid)
{
ll ans = 0;
for (int i = 1; i <= n;i++)
{
if(a[i].s<=mid)
{
ans += (min(a[i].e, mid) - a[i].s) / a[i].d + 1;
}
}
return ans;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while(t--)
{
cin >> n;
ll l = 0, r = 0;
for (int i = 1; i <= n;i++)
{
cin >> a[i].s >> a[i].e >> a[i].d;
r = max(r, a[i].e);
}
while(l<r)
{
ll mid = l + r >> 1;
if(check(mid)&1)
r = mid;
else
l = mid + 1;
}
ll ans = check(r) - check(r - 1);
if(ans&1)
{
cout <<r<<' '<< ans<<'\n';
}
else
cout << "There's no weakness." << '\n';
}
return 0;
}
AcWing 121. 赶牛入圈
昨晚没写出来,今早看了题解,在处理二维前缀和的时候没处理好,因为草是在方格的中间而不是在点上,所以枚举的右下角的点还得-1。因为草的x,y可能很大,但实际上最多只有500单位的草,利离散化,二维前缀和处理草坐标,二分长度,定义k为二分到的长度大于mid的右下角的点,开始枚举以该点为右下角的矩阵的草的单位和。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=1000+10,INF=1e8;
int a[N],b[N],uni[N],sum[N][N],c,n,m;
bool check(int mid)
{
int k = lower_bound(uni + 1, uni + 1 + m, mid) - uni;
k--;
for (int x = k; x <= m;x++)
{
for (int y = k; y <= m;y++)
{
int x1 = upper_bound(uni + 1, uni + 1 + x, uni[x] - mid) - uni;
int y1 = upper_bound(uni + 1, uni + 1 + y, uni[y] - mid) - uni;
if(sum[x][y]-sum[x1-1][y]-sum[x][y1-1]+sum[x1-1][y1-1]>=c)
return 1;
}
}
return 0;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int cnt=0;
cin >> c >> n;
for (int i = 1; i <= n;i++)
{
cin >> a[i] >> b[i];
uni[++cnt] = a[i];
uni[++cnt] = b[i];
}
sort(uni + 1, uni + 1 + cnt);
m = unique(uni + 1, uni + 1 + cnt) - uni - 1;
for (int i = 1; i <= n;i++)
{
int x = lower_bound(uni + 1, uni + 1 + m, a[i]) - uni;
int y = lower_bound(uni + 1, uni + 1 + m, b[i]) - uni;
sum[x][y]++;
}
for (int i = 1; i <= m;i++)
{
for (int j = 1; j <= m;j++)
sum[i][j] += sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1];
}
int l=1,r=10000;
while(l<r)
{
int mid=(l+r)>>1;
if(check(mid))
{
r=mid;
}
else
{
l=mid+1;
}
}
cout << r;
return 0;
}