牛客nowcoder NOIP普及组第三场
qtmd AK了
直接题解吧
A-十七边形
牛牛想在一个半径为r的圆中,找到一个内接的十七边形,使他的面积最大。输入半径r,输出最大的面积。 1 <= r <= 10000
在10组数据中,存在5组数据,半径为1,10,100,1000,10000。 换句话说,对于50%的数据,r是10的次幂。
输出保留6位小数
Solution:肯定是正十七边形喽
xjb证下:内接的十七边形一定把圆心角分为17份
那么根据叉积算面积,\(\displaystyle S=\frac {r^2} 2 \sum_{i=1}^{17}\sin(\theta_i)\),然后不会证了,反正十七个\(\theta\)相等一定是坠吼的
#include <bits/stdc++.h>
using namespace std;
int r;
int main()
{
scanf("%d", &r);
double fk = acos(-1) * 2 / 17.0;
double area = r * r * sin(fk);
area = area / 2.0 * 17;
printf("%f\n", area);
return 0;
}
B-首都
在平面上有n个整点(横纵坐标都是整数) 牛牛想找到一个整点,使得这个点,到所有点的距离之和最小。 两个点的距离定义为从一个点走到另一个点的最小步数。 其中每步可以走向相邻8个点(上,下,左,右,左上,左下,右上,右下,类似国际象棋中的王)走一步。 输出这个最小的距离之和,和这个点选择的方案数。(即有多少个点,可以达到这个最小的距离)
对于100%的数据,1 <= n <= 100000,|x|, |y| <= 1000000000 对于40%的数据,1 <= n <= 100,|x|, |y| <= 100 对于以上每部分数据,都有50%的数据n是奇数。
Sol:将坐标系顺时针旋转45度再放大一个\(\sqrt2\),根据线性变换理论,\((x,y)\rightarrow(x+y,x-y)\)
这样切比雪夫距离变味了曼哈顿距离,然后根据中位数定理求解
需要分各种情况讨论
N是奇数,根据中位数定理,只有一个点是最小值。但是,但是,这个点可能不合法。。。我们再逆变换变换回去,求\(A=\begin{pmatrix}1&1\\1&-1\end{pmatrix}\)的逆矩阵。行列式\(\det\begin{vmatrix}1&1\\1&-1\end{vmatrix}=-2\),伴随矩阵\(A^*=\begin{pmatrix}-1&1\\1&1\end{pmatrix}\),逆矩阵为\(\begin{pmatrix}\frac 1 2&-\frac 1 2\\-\frac 1 2&-\frac 1 2\end{pmatrix}\),显然我们需要两个数的奇偶性相同才行。不会用线性代数利器--矩阵推的同学可以画图观察,很显然的结论。
对于N是奇数,检验中间点坐标是否合格,如果合格那就是他了,直接暴力算结果。如果不合格,我们上下左右四个点坐标一定合格(可以自己画图看看),算那四个点坐标,找最小的输出就行了。
对于N是偶数,那么根据中位数定理,最小值是一段二维的区间。如果这个二维的区间缩成了一个点,用上面的思路处理就行了。否则这个区间一定包含合法的点。而且这个区间内的值是相同的,统计合法的点的数目,直接输出就行了。具体实现可以看代码。
#include <bits/stdc++.h>
using namespace std;
int n;
long long x[100010], y[100010];
//最优解在一个点,如果那个点不合法,找他四个相邻的点
long long calc(long long xx, long long yy)
{
long long res = 0;
for (int i = 1; i <= n; i++)
res += abs(x[i] - xx) + abs(y[i] - yy);
return res;
}
//偶数
long long calc_even(long long l, long long r)
{
long long len = (r - l + 1);
if (len % 2 == 0)
return len / 2;
else
{
if ((l & 1) && (r & 1))
return len / 2;
else
return len / 2 + 1;
}
}
//奇数
long long calc_odd(long long l, long long r)
{
long long len = (r - l + 1);
if (len % 2 == 0)
return len / 2;
else
{
if ((l & 1) && (r & 1))
return len / 2 + 1;
else
return len / 2;
}
}
void work1()
{
int mx = x[(n + 1) / 2], my = y[(n + 1) / 2];
if ((mx + my) & 1)
{
long long res[4];
res[0] = calc(mx - 1, my);
res[1] = calc(mx + 1, my);
res[2] = calc(mx, my - 1);
res[3] = calc(mx, my + 1);
sort(res, res + 4);
cout << res[0] / 2 << endl;
int t = 1;
if (res[0] == res[1])
{
t++;
if (res[1] == res[2])
{
t++;
if (res[2] == res[3])
{
t++;
}
}
}
cout << t << endl;
}
else
{
long long res = calc(mx, my);
cout << res / 2 << endl << 1 << endl;
}
}
void work0()
{
long long x1 = x[n / 2], x2 = x[n / 2 + 1];
long long y1 = y[n / 2], y2 = y[n / 2 + 1];
//如果[x1, x2]或[y1, y2]区间长度大于1就钦定了
//当[x1, x2]和[y1, y2]都是一个点
//才用想上面那样特盘
if (x1 == x2 && y1 == y2)
{
if ((x1 + y1) & 1)
{
long long res[4];
res[0] = calc(x1 - 1, y1);
res[1] = calc(x1 + 1, y1);
res[2] = calc(x1, y1 - 1);
res[3] = calc(x1, y1 + 1);
sort(res, res + 4);
cout << res[0] / 2 << endl;
int t = 1;
if (res[0] == res[1])
{
t++;
if (res[1] == res[2])
{
t++;
if (res[2] == res[3])
{
t++;
}
}
}
cout << t << endl;
}
else
{
long long res = calc(x1, y1);
cout << res / 2 << endl << 1 << endl;
}
}
else
{
//否则这个二维区间内一定存在至少一个合法数字。
//现在任务是给定[x1, x2]和[y1, y2]求有多少合法数字。
//对于一个合法数字,一定是两个偶数或者两个奇数组成。
//所以只需要统计闭区间奇数和偶数的个数。
long long evenx = calc_even(x1, x2);
long long oddx = calc_odd(x1, x2);
long long eveny = calc_even(y1, y2);
long long oddy = calc_odd(y1, y2);
long long res = evenx * eveny + oddx * oddy;
long long ans = calc(x1, y1);
cout << ans / 2 << endl << res << endl;
}
}
int main()
{
scanf("%d", &n);
for (int xx, yy, i = 1; i <= n; i++)
{
scanf("%d%d", &xx, &yy);
x[i] = xx + yy;
y[i] = xx - yy;
}
sort(x + 1, x + 1 + n);
sort(y + 1, y + 1 + n);
if (n & 1)
work1();
else
work0();
return 0;
}
C-分则能成
牛牛刚开始有一个正整数n。每次操作牛牛可以选择一个自己有的数字x,把x分为两正整数y和z,需满足x=y+z,然后获得y*z的收益。 (当然,在这个过程中,牛牛会失去x这个数字,并且获得y和z这2个数字。)
牛牛一共可以分k次,牛牛希望最大化这k次的收益之和。 因为分割的结果y和z是正整数,所以选择的x必须>=2。
对于100%的数据,1 <= k < n <= 109 对于40%的数据,1 <= k < n <= 10 对于70%的数据,1 <= k < n <= 100
Solution:(upd:所有的k改成了k+1)
打表发现,我们把N拆成k+1个均匀的数,xjb合并即可。
这些数的平均数是\(\displaystyle \frac N {k+1}\)(废话)
如果那个\(\displaystyle \frac N {k+1}\)是整数,那就是k+1个这数字喽。
如果\(\displaystyle \frac N {k+1}\)不是一个整数,那么
较小的数为\(\displaystyle \left\lfloor \frac N {k+1}\right\rfloor\),有\(\displaystyle {(k+1)}\left\lceil \frac N {k+1}\right\rceil-N\)个。
较大的数为\(\displaystyle \left\lceil\frac N {k+1}\right\rceil\),有\(\displaystyle N-{(k+1)}\left\lfloor\frac N {k+1}\right\rfloor\)个。(by GMPotlc: 这tmd就是\(N\mod(k+1)\)啊,你个sb)
同理,xjb合并即可。
合并的计算可以用分治,log级别。
全程开long long
#include <bits/stdc++.h>
using namespace std;
//合并k个x得到的exp
//分治。。。log级别的
long long merge(long long x, long long k)
{
//一个合并个毛线
if (k == 1)
return 0;
if (k & 1)//k/2, k/2, 1
{
long long res = merge(x, k / 2);
long long yg = x * (k / 2);
return res * 2 + yg * yg + 2 * yg * x;
}
else
{
//k/2, k/2
long long res = merge(x, k / 2);
long long yg = x * (k / 2);
return res * 2 + yg * yg;
}
}
int main()
{
long long n, k;
cin >> n >> k;
k++;
if (n % k == 0)
{
long long fuck = n / k;
cout << merge(fuck, k) << endl;
}
else
{
long long fuck1 = n / k, fuck2 = fuck1 + 1;
//向下取整,向上取整
long long t1 = k * fuck2 - n, t2 = n - k * fuck1;
long long res1 = merge(fuck1, t1);
long long res2 = merge(fuck2, t2);
long long tot1 = t1 * fuck1, tot2 = t2 * fuck2;
long long res3 = tot1 * tot2;
cout << res1 + res2 + res3 << endl;
}
return 0;
}
upd:dp打表程序,加一个#define csv
就行
#include <bits/stdc++.h>
using namespace std;
int f[110][110];
int main()
{
int n, k;
scanf("%d%d", &n, &k);
for (int x = 2; x <= n; x++)
for (int y = 1; y <= k; y++)
{
for (int i = 1; i < x; i++)//i, x - i
for (int j = 0; j < y; j++)//j, y - j - 1
f[x][y] = max(f[x][y], f[i][j] + f[x - i][y - j - 1] + i * (x - i));
}
printf("%d\n", f[n][k]);
#ifdef csv
freopen("data.csv", "w", stdout);
printf("he,");
for (int i = 0; i <= 100; i++)
printf("[%d],", i);
printf("\n");
for (int i = 0; i <= 100; i++)
{
printf("-%d-,", i);
for (int j = 0; j <= 100; j++)
printf("%d,", f[i][j]);
printf("\n");
}
#endif
return 0;
}
D-戴德兰
改了题面就是水题了
#include <bits/stdc++.h>
using namespace std;
int n, c, d, a[10010];
int main()
{
scanf("%d%d%d", &n, &c, &d);
c += d;
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
sort(a + 1, a + 1 + n);
for (int i = 1; i <= n; i++)
{
if (c - a[i] < 0)
{
printf("%d\n", i - 1);
break;
}
c -= a[i];
}
return 0;
}