Kingdom And Dice
(SRM548 Div1Medium)
1.{ai}中是0的位置可以修改成其他的数[0,x]或者不修改。
2.求p即求(每个ai>bj的个数总和)/(n*n)。
3.求使|p-0.5|最小的p,∵考虑到n<=50,∴(每个ai>bj的个数总和)<=n*n<=2500,可以求出所有可能的p,然后取min。
4.一个ai的贡献是小于ai的b的个数。
5.由于x<=1e9,需要对原数组进行离散
6.设dp[i]:总贡献为i时,最少需要改变多少个ai。设s[i]:在[B[i]+1,B[j]-1]中有多少个数
7.dp转移就是简单的多重背包
for(int j=1;j<=n;j++){
for(int k=s[j];k>=1;k--){
for(int i=n*n;i>=j;i--){//必须倒着
dp[i]=min(dp[i],dp[i-j]+1);
}
}
}
8.发现s[i]的总和相当于[Bmin,Bmax]的区间大小,虽然可以水过去,而且还挺快,但是毕竟不够严谨,可以进行一些优化。用二进制优化多重背包,发现重复s[j]次塞入的是同一样物品,因此可以把这类东西分成log2(s[i])个独立物品,这样塞入物品的个数为log个,复杂度就有保障了
for(int i=1;i<=n;i++){
int t=1;//枚举个数
while(1) {
if(s[i]>=t) {
s[i]-=t;
val[++len]=t*j;
wight[len]=min(all,t);//all是a=0的个数
} else {
if(s[i]>0) {//剩余不足的也算一个
val[++len]=tmp*j;
wight[len]=min(all,tmp);
}
break;
}
t=t+t;
}
}
9.那么dp就是01背包了。
dp[0]=0;
for(int i=1; i<=n*n; i++)dp[i]=55;//init
for(int k=1; k<=len; k++) {//len为物品个数
for(int i=n*n; i>=val[k]; i--) {
dp[i]=min(dp[i],dp[i-val[k]]+wight[k]);
}
}
10.pay attention !求答案是请勿直接用double存,将|p-0.5|整数求最小值,最后再除以n^2,不然容易被卡精度。
int p=1e9;
for(int i=0; i<=n*n; i++) {
if(dp[i]>all)continue;
int sum=res+i;
if(abs(2*sum-n*n)<abs(2*p-n*n)) {
p=sum;
}
}
printf("%.6f\n",1.0*p/n/n);