A - Can you solve this equation?
[Problem]
询问 8*x^4 + 7*x^3 + 2*x^2 + 3*x + 6 == Y 的 小数解
[Solution]
显然,该函数单调,直接二分答案即可,最近get到二分新技巧,无脑for循环100次
[Code]
#include<cstdio>
#include<iostream>
#include<map>
#include<cmath>
using namespace std;
map<int, string> fx;
int y;
double cal(double x)
{
double tot = 8 * x * x * x * x + 7 * x * x * x + 2 * x * x + 3 * x + 6 - y;
return tot ;
}
bool calc(double x)
{
double tot = 8 * x * x * x * x + 7 * x * x * x + 2 * x * x + 3 * x + 6 - y;
return tot >= 0;
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
scanf("%d", &y);
double l = 0, r = 100;
double k = (cal(0) * cal(100));
if (k > 0)
{
printf("No solution!\n");
continue;
}
double ans = -1;
for(int i = 1; i <= 100; i++)
{
// printf("%.4f %.4f\n", l, r);
double mid = (l + r) / 2;
if (calc(mid))
{
ans = mid;
r = mid;
} else
l = mid;
}
printf("%.4f\n", ans);
}
return 0;
}
B - The Meeting Place Cannot Be Changed
[problem]
给定n个人在数轴上的位置,每个人可以选择往左走、往右走,也可静止不懂,询问n个人最短多少时间相遇
[Solution]
二分时间,通过O(n)的时间处理出每个人在mid时间后位置的可能区间,最后求一下n个区间的交,如果不为空,则r=mid-1,否则l=mid + 1
[Code]
#include<cstdio>
#include<iostream>
#include<map>
#include<cmath>
using namespace std;
map<int, string> fx;
const int N = 60000 + 500;
double a[N], v[N];
int n;
bool calc(double mid)
{
double ll = a[1] - mid * v[1];
double rr = a[1] + mid * v[1];
for(int i = 2; i <= n; i++)
{
double s = a[i] - mid * v[i];
double t = a[i] + mid * v[i];
ll = max(ll, s);
rr = min(rr, t);
if (ll > rr)
return false;
}
return true;
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++)
scanf("%lf", &a[i]);
for(int i = 1; i <= n; i++)
scanf("%lf", &v[i]);
double l = 0, r = 1000000000;
double ans = -1;
for(int i = 1; i <= 100; i++)
{
// printf("%.4f %.4f\n", l, r);
double mid = (l + r) / 2;
if (calc(mid))
{
ans = mid;
r = mid;
} else
l = mid;
}
printf("%.12f", ans);
return 0;
}
C - Cable master
[Solution]
经典的二分题目
[Ps]
1.注意此题槽点比较多,首先题目描述的centimeter与kilometer这种需要注意,l的起始位置为
0.01
2.mgj,最后输出的时候不能四舍五入,需要舍去后面的一些位
#include<cstdio>
#include<iostream>
#include<map>
#include<cmath>
using namespace std;
map<int, string> fx;
const int N = 60000 + 500;
double a[N], v[N];
int n;
bool calc(double mid)
{
double ll = a[1] - mid * v[1];
double rr = a[1] + mid * v[1];
for(int i = 2; i <= n; i++)
{
double s = a[i] - mid * v[i];
double t = a[i] + mid * v[i];
ll = max(ll, s);
rr = min(rr, t);
if (ll > rr)
return false;
}
return true;
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++)
scanf("%lf", &a[i]);
for(int i = 1; i <= n; i++)
scanf("%lf", &v[i]);
double l = 0, r = 1000000000;
double ans = -1;
for(int i = 1; i <= 100; i++)
{
// printf("%.4f %.4f\n", l, r);
double mid = (l + r) / 2;
if (calc(mid))
{
ans = mid;
r = mid;
} else
l = mid;
}
printf("%.12f", ans);
return 0;
}
D - Median
[Problem]
给定n个整数,这n个整数可以形成C(n,2)个|ai - aj|,询问中位数
[Solution]
我们先将序列a排序
显然,我们固定一个元素i,则a[i + 1] - a[i], a[i + 2] - a[i], …a[n] - a[i] 升序,这样会形成n-1个有序序列,询问这所有数中的中位数,我们二分中位数,每次确保比mid小的数的个数,最后列加一下,与1/2总数比较一下即可,注意统计比mid小的时候仍需要二分,可以用STL
lower_bound(a + 1, a + n, x) - a; //返回[1, n - 1]中第一个大于等于x的下标
upper_bound(a + 1, a + n, x) - a; // 返回[1, n - 1]中第一个大于x的下标
[Code]
#include<cstdio>
#include<iostream>
#include<map>
#include<cmath>
#include<algorithm>
using namespace std;
map<int, string> fx;
const int N = 100000 + 500;
int a[N], c[N];
int n;
long long m;
bool calc(int mid)
{
long long ans = 0;
for(int i = 1; i < n; i++)
{
int x = upper_bound(a + i + 1, a + n + 1, a[i] + mid) - a;
ans += x - i - 1;
if (ans >= m)
return true;
}
return false;
}
int main()
{
while(~scanf("%d", &n))
{
int kk = (n - 1) * n / 2;
m = 1LL * (kk + 1) / 2;
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]);
sort(a + 1, a + n + 1);
int l = 0, r = 1000000000;
int ans = -1;
while(l <= r)
{
// printf("%d %d\n", l, r);
int mid = (l + r) >> 1;
if (calc(mid))
{
r = mid -1;
ans = mid;
}
else
l = mid + 1;
}
printf("%d\n", ans);
}
return 0;
}
E - NPY and shot
[Solution]
三分。
目标函数为凸函数,用三分求解即可,最近get三分技巧,
[templet]
for(int i = 1;i <= 100; i++)
{
double mid = l + (r - l) / 3;
double mmid = r - (r - l) / 3;
if (calc(mid) > calc(mmid)) //在凸函数的情况下,mmid和r均在极值的右侧
r = mmid; //
else
l = mid;
}
[Code]
#include<cstdio>
#include<iostream>
#include<map>
#include<cmath>
#include<algorithm>
using namespace std;
map<int, string> fx;
const int N = 100000 + 500;
int a[N], c[N];
int n;
long long m;
double v0, H;
const double pi = acos(-1.0);
double calc(double p)
{
double t1, t2, h1, t, v1, v2;
v1 = v0 * sin(p);
v2 = v0 * cos(p);
t1 = v1 / 9.8;
h1 = v1 * t1 / 2 + H;
t2 = sqrt(2 * h1 / 9.8);
t = t1 + t2;
return t * v2;
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
scanf("%lf%lf", &H, &v0);
double tot = calc(pi / 4);
double l = 0, r = pi / 2;
for(int i = 1; i <= 100; i++)
{
double mid = l + (r - l) / 3;
double mmid = r - (r - l) / 3;
if (calc(mid) > calc(mmid))
r = mmid;
else
l = mid;
}
double ans = calc(l);
printf("%.2f\n", ans);
}
return 0;
}
F - Robin Hood
[Problem]
给定n个数,每次操作把最大的数减一,最小的数加一,询问k次操作后数组最大值与最小值差值。
[Solution]
对于每次操作,一个数加一,一个数减一,我们把所有的数整体看待的话,相当于从最大的数开始,减掉k个一,使得最大值尽可能地小,同理,另一边对应的操作是使最小值尽可能地大,这样用两个二分就可以解决了,注意差值为0和为负值的情况,如果判断可以满足差值为0,则差值为0,否则差值为1
[Code]
#include<cstdio>
#include<iostream>
#include<map>
#include<cmath>
#include<algorithm>
using namespace std;
const int N = 500000 + 500;
int a[N];
int n, m;
bool calc1(int mid)
{
int tot = 0;
for(int i = 1; i <= n; i++)
if (a[i] < mid)
{
tot += mid - a[i];
if (tot > m)
return false;
}
return true;
}
bool calc2(int mid)
{
int tot = 0;
for(int i = 1; i <= n; i++)
if (a[i] > mid)
{
tot += a[i] - mid;
if (tot > m)
return false;
}
return true;
}
int main()
{
scanf("%d%d", &n, &m);
long long sum = 0;
for(int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
sum += a[i];
}
int ans1 = -1;
int l = 1, r = 1000000000;
while(l <= r)
{
int mid = (l + r) >> 1;
if (calc1(mid))
{
ans1 = mid;
l = mid + 1;
}else
r = mid -1 ;
}
int ans2 = -1;
l = 1;
r = 1000000000;
while(l <= r)
{
int mid = (l + r) >> 1;
if (calc2(mid))
{
ans2 = mid;
r = mid - 1;
}else
l = mid + 1;
}
int ans = ans2 - ans1;
if (ans <= 0)
if (sum % n == 0)
ans = 0;
else
ans = 1;
printf("%d", ans);
return 0;
}
G - Vasya and String
[Solution]
尺取法,自己的姿势貌似不太清晰,导致wa了好多次
[Code]
#include<cstdio>
#include<iostream>
using namespace std;
const int N = 100000 + 500;
string s;
int n, m;
int main()
{
// freopen("b.in", "r", stdin);
int n, m;
scanf("%d%d", &n, &m);
cin>>s;
int l = 0, r = 0;
int ans = 0;
int sum = 0;
if (s[0] == 'a')
sum++;
while(true)
{
while(sum <= m && r < n)
{
ans = max(ans, r - l + 1);
if (s[r + 1] == 'a')
sum++;
r++;
}
if (r > n - 1)
break;
while(sum > m && l < r)
{
if (s[l] == 'a')
sum--;
l++;
}
if (sum <= m)
ans = max(ans, r - l + 1);
if (r >= n - 1)
break;
if (sum > m)
{
l = r + 1;
r++;
if (s[r] == 'a')
sum = 1;
else
sum = 0;
}
}
l = 0; r = 0; sum = 0;
if (s[0] == 'b')
sum++;
while(true)
{
while(sum <= m && r < n)
{
ans = max(ans, r - l + 1);
if (s[r + 1] == 'b')
sum++;
r++;
}
if (r > n - 1)
break;
while(sum > m && l < r)
{
if (s[l] == 'b')
sum--;
l++;
}
if (sum <= m)
ans = max(ans, r - l + 1);
if (r >= n - 1)
break;
if (sum > m)
{
l = r + 1;
r++;
if (s[r] == 'b')
sum = 1;
else
sum = 0;
}
}
printf("%d", ans);
return 0;
}