首页 > 其他分享 >[IOI2000] 邮局

[IOI2000] 邮局

时间:2023-10-09 11:01:24浏览次数:36  
标签:md IOI2000 long 邮局 int 村庄 dp

[IOI2000] 邮局

题目描述

高速公路旁边有一些村庄。高速公路表示为整数轴,每个村庄的位置用单个整数坐标标识。没有两个在同样地方的村庄。两个位置之间的距离是其整数坐标差的绝对值。

邮局将建在一些,但不一定是所有的村庄中。为了建立邮局,应选择他们建造的位置,使每个村庄与其最近的邮局之间的距离总和最小。

你要编写一个程序,已知村庄的位置和邮局的数量,计算每个村庄和最近的邮局之间所有距离的最小可能的总和。

\(n\le 5\times 10^5\)

1. \(n\le 300\)

定义 \(dp_{i,j}\) 为前 \(i\) 个村庄,用了 \(j\) 个邮局的最小距离可能。
我们把那些最近的邮局为同一个的称为一段,那么我们可以枚举上一段的开头。
对于某一段 \([l,r]\) ,邮局一定是放在中位数那里,然后预处理 \(a\) 的前缀和就可以了。

2. \(n\le 3000\)

观察到那个式子是由决策单调性的,所以可以用二维决策单调性优化。

换一下,定义 \(dp_{i,j}\) 为用了 \(i\) 个邮局,前 \(j\) 个村庄的最小值,\(p_{i,j}\) 为他的最优决策点是 \(dp_{i,p_{i,j}}\) 转移过来。那么一定满足 \(p_{i-1,j}<p_{i,j}\le p_{i,j+1}\),那么可以只在这个区间内扫描。均摊复杂度 \(O(n^2)\)

#include<bits/stdc++.h>
using namespace std;
const int N=3005,M=305;
typedef long long LL;
int read()
{
	int s=0;
	char ch=getchar();
	while(ch<'0'||ch>'9')
		ch=getchar();
	while(ch>='0'&&ch<='9')
		s=s*10+ch-48,ch=getchar();
	return s;
}
int n,a[N],m,p[M][N];
LL dp[M][N],s[N];
LL ask(int l,int r)
{
	int md=l+r>>1;
	return s[r]-s[md]-1LL*(r-md)*a[md]+(md-l+1LL)*a[md]-s[md]+s[l-1];
}
int main()
{
	n=read(),m=read();
	for(int i=1;i<=n;i++)
		s[i]=s[i-1]+(a[i]=read());
	sort(a+1,a+n+1);
	memset(dp,0x3f,sizeof(dp));
	dp[0][0]=0;
	for(int i=1;i<=m;i++)
	{
		p[i][n+1]=n;
		for(int j=n;j>=1;j--)
			for(int k=max(p[i-1][j],1);k<=p[i][j+1]&&k<=j;k++)
				if(dp[i-1][k-1]+ask(k,j)<dp[i][j])
					p[i][j]=k,dp[i][j]=dp[i-1][k-1]+ask(k,j);
	}
	printf("%lld",dp[m][n]);
	return 0;
			
}

3.$ n\le 5\times 10^5$

要选严格 \(k\) 个邮局,想到 WQS 二分。打表可知函数是凸的。

然后方程变成 1 维,可以知道这个是满足决策单调性的,二分队列即可。

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;
int l=0,r=1000000000,n,m,a[N],c[N];
long long dp[N],s[N];
struct node{
	int l,r,x;
}q[N];
long long ask(int l,int r)
{
	int md=l+r>>1;
	return dp[l-1]+s[r]-s[md]-1LL*(r-md)*a[md]+(md-l+1LL)*a[md]-s[md]+s[l-1];
}
int erfen(int x,int y,int l,int r)//x 
{
	++r;
	while(l<=r)
	{
		int md=l+r>>1;
		if(ask(x,md)>=ask(y,md))
			l=md+1;
		else
			r=md-1;
	}
	return l;
}
int ok(int x)
{
	int l,r;
	memset(dp,0x7f,sizeof(dp));
	memset(c,0,sizeof(c));
	q[l=r=1]=(node){1,n,1};
	dp[0]=0;
	for(int i=1;i<=n;i++)
	{
		if(q[l].r==i-1)
			++l;
		dp[i]=ask(q[l].x,i)+x;
		c[i]=c[q[l].x-1]+1;
		q[l].l=i;
		if(l>r||ask(i+1,q[r].r)<ask(q[r].x,q[r].r))
		{
			while(l<=r&&ask(i+1,q[r].l)<ask(q[r].x,q[r].l))
				--r;
			int k=erfen(i+1,q[r].x,q[r].l,q[r].r);
			q[r].r=k-1;
			q[++r]=(node){k,n,i+1};
		}
	}
	return c[n]<=m;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d",a+i),s[i]=s[i-1]+a[i];
	while(l<=r)
	{
		int md=l+r>>1;
		if(ok(md))
			r=md-1;
		else
			l=md+1;
	}
	ok(l);
	printf("%d",dp[n]-m*l);	
}

标签:md,IOI2000,long,邮局,int,村庄,dp
From: https://www.cnblogs.com/mekoszc/p/17750980.html

相关文章

  • 决策单调性优化DP 学习笔记 & P4767 [IOI2000] 邮局 题解
    0.题面题目描述高速公路旁边有一些村庄。高速公路表示为整数轴,每个村庄的位置用单个整数坐标标识。没有两个在同样地方的村庄。两个位置之间的距离是其整数坐标差的绝对值。邮局将建在一些,但不一定是所有的村庄中。为了建立邮局,应选择他们建造的位置,使每个村庄与其最近的邮局......
  • [IOI2000] 邮局
    题目描述高速公路旁边有一些村庄。高速公路表示为整数轴,每个村庄的位置用单个整数坐标标识。没有两个在同样地方的村庄。两个位置之间的距离是其整数坐标差的绝对值。邮局将建在一些,但不一定是所有的村庄中。为了建立邮局,应选择他们建造的位置,使每个村庄与其最近的邮局之间的距......
  • 邮局--dp经典问题
    题目:http://poj.org/problem?id=1160题意: 一些村庄被建立在一条笔直的高速公路边上,我们用一条坐标轴来描述这条高速公路,每一个村庄的坐标都是整数,没有两个村庄坐标相同。两个村庄间的距离,定义为它们的坐标值差的绝对值。我们需要在一些村庄建立邮局——当然,并不是每一个村庄都......
  • 外汇天眼:高雄又现假亲友借钱诈骗,邮局及时通报成功阻汇款!
    过去几年诈骗犯罪案件急速增长,而且手法与套路愈来愈多元,令人防不胜防。与此同时,那些看似老套的假亲友、猜猜我是谁等伎俩依然存在,并且还是有许多民众因此上当。上个礼拜,高雄警方就与辖区内邮局合作阻止一位老妇人受骗。据了解,5/22三民区民族派出所接到邮局的通报电话,行员表示现场......
  • 宝塔邮局-并解决A纪录解析失败问题
    为什么一定要用这个邮局呢,只要是方便,在宝塔面板直接安装就行了。使用教程如下:https://www.bt.cn/bbs/thread-87496-1-1.html有一个BUG本来已经设置好了,但是总显示A记录......
  • [IOI2000]邮局 题解
    简要题意线段上有\(V\)个村庄,现在要建\(P\)个邮局,使每个村庄到最近的邮局的距离之和最小。50分做法设\(dp[i][j]\)表示第一个村庄到第\(i\)个村庄,建了\(j\)个......
  • 时光邮局|来写一封未来的信试试吧!一个我的新项目
    什么是时光邮局?漫漫星河璀璨,漫漫古道长河。官网:云寄-时光邮局寻找一份特殊的意义,学会热爱生活,学会面朝大海。有一天我收到了两年前的自己来信。如果可以给末来寄信......
  • 邮局寄包裹
    【题目描述】小明去邮局给朋友寄礼物。发现邮局对邮寄包裹的费用是这样规定的:如果包裹长宽高任意一个尺寸超过1米,或重量超过30千克,不予邮寄;对可以邮寄的包裹每件收手续费0.2......