牛客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;
}
posted @ 2018-09-22 11:33  ghj1222  阅读(298)  评论(1编辑  收藏  举报