牛客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 
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 
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 
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 
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 
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;
}

转载于:https://www.cnblogs.com/oier/p/9689742.html

你可能感兴趣的:(牛客nowcoder NOIP普及组第三场)