2020牛客暑期多校训练营(第七场)A.Social Distancing(计算几何 dp/打表)

题目

T(T<=250)组样例,每次给出一个圆的半径r(r<=30),

在圆上和圆内放置n个整点,要求\sum_{i=1}^{n-1}\sum_{j=i+1}^{n}d(i,j)^{2}的最大值。

其中d(i,j)表示i和j之间的距离。即求所有点的距离的平方和的最大值。

思路来源

https://blog.csdn.net/zhangchizc/article/details/107746793

题解

xy分开考虑,不妨只考虑x这一维

考虑这是一个n*n矩阵的上三角矩阵(略有不同,此处对角线均为0)

对于任意一个数i来说,其与j \neq i的j都出现过一次(i,j)对,代表(x_{i}-x_{j})*(x_{i}-x_{j})

二者乘积是x_{i}(x_{1}+x_{i-1}+...+x_{i+1}+...+x_{n}),把xi这项凑足,同时记sum=\sum_{i=1}^{n}x_{i}

则有每个i对答案的贡献是-x_{i}*(sum-x_{i})

可以理解为平方项二倍展开-2*x_{i}*\sum x_{j}(j \neq i),上三角矩阵只有一半,所以把2除掉

且注意到x_{i}出现在n-1(i,j)对里,平方项贡献是(n-1)x_{i}^{2}

\sum_{i=1}^{n-1}\sum_{j=i+1}^{n}(x_{i}-x_{j})^{2}即化简为(n-1)\sum_{i=1}^{n}x_{i}^{2}-\sum_{i=1}^{n}(sum-x_{i})*x_{i}=n*\sum_{i=1}^{n}x_{i}^{2}-sum*sum

感觉这个套路见过很多次了,然而想不到要这么dp,

\sum_{i=1}^{n-1}\sum_{j=i+1}^{n}d(i,j)^{2} =\sum_{i=1}^{n-1}\sum_{j=i+1}^{n}(x_{i}-x_{j})^{2}+(y_{i}-y_{j})^{2}

于是就化简为n*\sum_{i=1}^{n}(x_{i}^{2}+y_{i}^2)-(\sum_{i=1}^{n}x_{i})^{2}-(\sum_{i=1}^{n}y_{i})^{2}

令dp[i][j][k]表示选了i个点,sumx=j,sumy=k(把后两项固定住)时的最大第一项

则实际答案为i*dp[i][j][k]-j*j-k*k,具体实现时将点按半径r的增序更新,即可更新ans[i][r],

O(1e9)预处理,O(1)回答,5s还可以吧,要是实在太慢就打表

代码

#include
typedef long long ll;
using namespace std;
const int N=64*64+5,off=300;
//dp[i][j][k]表示选i个点 sumx=j sumy=k时 的 最大的平方项代价之和
int t,n,d,dp[9][610][610],ans[9][32],c;
struct node{
    int d,x,y;
    bool operator<(node &x)const{
        return d0){
                        ans[i][r]=max(ans[i][r],dp[i][j+off][k+off]*i-j*j-k*k);
                    }
                }
            }
        }
    }
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&d);
        printf("%d\n",ans[n][d]);
    }
    return 0;
}

 

你可能感兴趣的:(#,#,牛客多校)