BZOJ2321 [BeiJing2011集训]星器

推了一天然后发现我推出来的和网上推出来的都不一样……

说说怎么做吧

左边的Yihan_Z神犇告诉我这题是物理,那既然是物理肯定是用势能分析了

先考虑一下一维情况

那么我们就定义两颗星之间的魔法势能为把这两个颗星从当前情况一直拉近,直到他们两个之间的距离为0所获得的魔法能

也就是d+d-2+d-4+.....,化简一下得(d^2+2d)/4,当两颗星重合时认为他俩距离是-1,那么这时候势能也是-1

定义整个星器的势能为任意两颗星之间的势能的和,那么考虑现在有一个一维的星器,我们把其中两颗星进行了一次操作

那么这两颗星之间的势能就减少了d,而对于除了这两颗星以外的任意一颗星,我们分这颗星在操作的两颗星同侧或者异侧分类讨论一下,发现第三颗星与这两颗星之间的势能的和一定减少了d/2

也就是说总共的势能一共减少了d+(n-2)*d/2,n是星的总数

这其中有d的能量转化为了我们自己的魔法能,而剩下的(n-2)*d/2的能量就转化为其他能量了

我们要最大化我们获得的魔法能,就相当于要最小化流失的能量,而我们发现这两个最优化事实上都是要令d最大,所以我们知道确定了初态和终态之后我们所能获得的魔法能是一定的

把d+(n-2)*d/2化简,得到d*n/2,也就是说初态和终态的势能差等于我们所获得的魔法能*n/2,那么这样算出初态和终态的势能我们就能知道我们能获得多少魔法能了

类似机械效率,你可以认为2/n是这个星器的魔法效率

那么接下来考虑一下二维的情况,我们要重新定义我们的势能,使得他依旧满足一些良好的性质,在一维情况下,一个很良好的性质是对两颗距离为d的星操作之后,任意第三颗星与这两颗星之间的势能的和一定减少了d/2,那么再二维情况下,我们定义两个星之间的势能为(dx^2+2*dx+dy^2+2*dy+1)/4,dx,dy分别为两颗星的横坐标之差与纵坐标只差,这样的话依旧满足上面提到的性质,并且当dy==-1的时候势能与一维情况下相等,所以这个势能函数依旧满足魔法效率等于2/n,所以我们用这个势能函数计算初末状态势能只差,然后*2/n即可得到答案

注意到横纵坐标之间是独立的,所以不妨对横纵坐标分别计算势能,那么我们就可以在星器边长立方的复杂度内计算势能了

这道题给我们的启示:即使是魔法能也不是无限的,我们要节约能源

#include<iostream>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<cstdlib>
#include<cstdio>
#include<map>
#include<bitset>
#include<set>
#include<stack>
#include<vector>
#include<queue>
using namespace std;
#define MAXN 210
#define MAXM 1010
#define ll long long
#define eps 1e-8
#define MOD 1000000007
#define INF 1000000000
ll n,m;
ll a[MAXN][MAXN],b[MAXN][MAXN];
ll s,tot;
ll sca[MAXN],sra[MAXN],scb[MAXN],srb[MAXN],sa,sb;
ll S(ll x){
	return x*x+2*x;
}
int main(){
	ll i,j,k,l;
	scanf("%lld%lld",&n,&m);
	for(i=1;i<=n;i++){
		for(j=1;j<=m;j++){
			scanf("%lld",&a[i][j]);
		}
	}
	for(i=1;i<=n;i++){
		for(j=1;j<=m;j++){
			scanf("%lld",&b[i][j]);
		}
	}
	for(i=1;i<=n;i++){
		for(j=1;j<=m;j++){
			tot+=a[i][j];
			s+=sa*a[i][j]-sb*b[i][j];
			for(k=1;k<=n;k++){
				ll d=abs(i-k)-1;
				s+=S(d)*(a[i][j]*sca[k]-b[i][j]*scb[k]);
			}
			for(k=1;k<=m;k++){
				ll d=abs(j-k)-1;
				s+=S(d)*(a[i][j]*sra[k]-b[i][j]*srb[k]);
			}
			s+=(-1ll)*a[i][j]*(a[i][j]-1)/2;
			s-=(-1ll)*b[i][j]*(b[i][j]-1)/2;
			sca[i]+=a[i][j];
			sra[j]+=a[i][j];
			scb[i]+=b[i][j];
			srb[j]+=b[i][j];
			sa+=a[i][j];
			sb+=b[i][j];
		}
	}
	printf("%lld\n",s/(tot*2));
	return 0;
}

/*

*/



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值