斜率优化第二次,运送猫猫

发布时间 2023-11-29 16:58:01作者: HL_ZZP

题目描述

Zxr960115 is owner of a large farm. He feeds mm cute cats and employs pp feeders. There's a straight road across the farm and nn hills along the road, numbered from 1 to nn from left to right. The distance between hill ii and (i−1)(i−1) is didi​ meters. The feeders live in hill 1.

One day, the cats went out to play. Cat ii went on a trip to hill hihi​ , finished its trip at time titi​ , and then waited at hill hihi​ for a feeder. The feeders must take all the cats. Each feeder goes straightly from hill 1 to nn without waiting at a hill and takes all the waiting cats at each hill away. Feeders walk at a speed of 1 meter per unit time and are strong enough to take as many cats as they want.

For example, suppose we have two hills (d2=1)(d2​=1) and one cat that finished its trip at time 3 at hill 2 (h1=2)(h1​=2) . Then if the feeder leaves hill 1 at time 2 or at time 3, he can take this cat, but if he leaves hill 1 at time 1 he can't take it. If the feeder leaves hill 1 at time 2, the cat waits him for 0 time units, if the feeder leaves hill 1 at time 3, the cat waits him for 1 time units.

Your task is to schedule the time leaving from hill 1 for each feeder so that the sum of the waiting time of all cats is minimized.
输入格式

The first line of the input contains three integers n,m,pn,m,p (2≤n≤105,1≤m≤105,1≤p≤100)(2≤n≤105,1≤m≤105,1≤p≤100).

The second line contains n−1n−1 positive integers d2,d3,...,dnd2​,d3​,...,dn​ (1≤di<104).(1≤di​<104).

Each of the next mm lines contains two integers hihi​ and titi​ (1≤hi≤n,0≤ti≤109)(1≤hi​≤n,0≤ti​≤109).
输出格式

Output an integer, the minimum sum of waiting time of all cats.

Please, do not write the %lld specifier to read or write 64-bit integers in С++. It is preferred to use the cin, cout streams or the %I64d specifier.
题意翻译

Zxr960115 是一个大农场主。

他养了 mm 只可爱的猫子,雇佣了 pp 个铲屎官。这里有一条又直又长的道路穿过了农场,有 nn 个山丘坐落在道路周围,编号自左往右从 11 到 nn。山丘 ii 与山丘 i−1i−1 的距离是 DiDi​ 米。铲屎官们住在 11 号山丘。

一天,猫子们外出玩耍。猫子 ii 去山丘 HiHi​ 游玩,在 TiTi​ 时间结束他的游玩,然后在山丘 HiHi​ 傻等铲屎官。铲屎官们必须把所有的猫子带上。每个铲屎官直接从 H1H1​ 走到 HnHn​,中间不停下,可以认为不花费时间的把游玩结束的猫子带上。每个铲屎官的速度为一米每单位时间,并且足够强壮来带上任意数量的猫子。

举个栗子,假装我们有两个山丘( D2=1D2​=1 ),有一只猫子,他想去山丘 22 玩到时间 33。然后铲屎官如果在时间 22 或者时间 33 从 11 号山丘出发,他就能抱走猫子。如果他在时间 11 出发那么就不行(猫子还在玩耍)。如果铲屎官在时间 22 出发,猫子就不用等他(ΔT=0ΔT=0)。如果他在时间 33 出发,猫子就要等他 11 个单位时间。

你的任务是安排每个铲屎官出发的时间(可以从 0 时刻之前出发),最小化猫子们等待的时间之和。

对于全部的数据,满足 2≤n≤1052≤n≤105,1≤m≤1051≤m≤105,1≤p≤1001≤p≤100,1≤Di<1041≤Di​<104,1≤Hi≤n1≤Hi​≤n,0≤t≤1090≤t≤109。
输入输出样例
输入 #1

4 6 2
1 3 5
1 0
2 1
4 9
1 10
2 10
3 12

输出 #1

3

CF311B Cats Transport
题目给的是每一只猫猫结束游玩的时间还有位置,但是其实这两个东西都是用来计算什么时候可以去接这只猫猫的
我们用一个数组a[i]表示什么时候时候让一个铲屎官出发可以刚刚好接到第i只猫猫
然后把a[i]从小到大排序,这样就直接把位置和结束时间这两个东西简化了
显然,我们 每一次派铲屎官出去都能够接到\(a[i]\)上连续的一段猫猫

不然dp都做不了。。因为如果不简化离散化都做不到,而\(t[i]<1e9\),这谁扛得住啊

这样就方便了,设\(f[i][j]\)表示已经用了j铲屎官接到了接前i只猫猫时的最小等待时间

方程很好写

\[f[i][j]= \displaystyle\max_{j-1\leq k \leq i-1}(f[k][j-1]+t[i]*(i-k)-sum(a[k],a[i])) \]

然后拆开来,把能拿出max的拿出来

\[f[i][j]=\displaystyle\max_{j-1\leq k \leq i-1}(f[k][j-1]+t[i]*i-t[i]*k-suma[i]+suma[k]) \]

发现里面有一个\(t[i]*k\) 的玩意,而且\(t[i]\)之间没有明显的规律,无法直接统计里面i变化时变化的东西来快速找到最优决策
但是这个i*k的形式是可以尝试斜率优化的
把max去掉,对式子进行一定的变形

\[f[i][j]=f[k][j-1]+t[i]*i-t[i]*k-suma[i]+suma[k]\\ -Suma[k]-f[k][j-1]=-t[i]*k-Suma[i]+t[i]*i-f[i][j]\\ f[k][j-1]+Suma[k]=t[i]*k+Suma[i]-t[i]*i+f[i][j] \]

基本出来了,可以滚动数组,但是没必要,t[i]没有单调性,要二分查找来找决策点,队头不需要出队,每次增加新元素的时候队尾出队就行

j放在外侧循环,是不变的,所以决策点不用因为这个出队
那如果j单调向右移动会不会导致不能做了?
好像不会啊,也是平衡树,然后决策前判断一下是否合法,不合法就删掉再找,反正平衡树删除也是\(log_2(n)\),每次都只进去一次,总体\(O(nlog(n))\)

已经...无所谓了

这题主要是前面的转化,我看到的时候其实就有点感觉,小猫的位置和结束时间只是共同决定了一个东西,既然这样,那为什么不直接把这个东西算出来呢?
很...朴实无华的想法,如果要难一些的话,可以把这个思路藏起来,不需要给这么明显的引导
那就真的难了

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll read() {
	char c=getchar();ll a=0,b=1;
	for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
	for(;c>='0'&&c<='9';c=getchar())a=a*10+c-48;return a*b;
}
ll n,m,p,pos[200001],Suma[200001];
ll f[200001][101];
ll q[200001],l,r;
struct cat
{
	ll t,h,Suma,a;
}a[200001];
bool mycmp(cat a,cat b)
{
	return a.a<b.a;
}
inline ll ry(ll x,ll j)
{
	return f[x][j-1]+a[x].Suma;
}
inline ll Y(ll a,ll b,ll c,ll j) //斜率 
{
	ll x1=a-b,  x2=b-c,  y1=ry(a,j)-ry(b,j),  y2=ry(b,j)-ry(c,j);
	return x1*y2-x2*y1;
}
inline ll calc(ll i,ll j,ll k)//y-kx
{
	return ry(k,j)-a[i].a*k;
}
int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	n=read(),m=read(),p=read();
//	cout<<m<<endl;
	for(ll i=2;i<=n;i++)pos[i]=pos[i-1]+read();
	for(ll i=1;i<=m;i++)
	{
		a[i].h=read(),a[i].t=read();
		a[i].a=a[i].t-pos[a[i].h];
	}
	sort(a+1,a+1+m,mycmp);
	for(ll i=1;i<=m;i++)a[i].Suma=a[i-1].Suma+a[i].a;
	for(ll i=1;i<=m;i++)
	{
		f[i][1]=a[i].a*i-a[i].Suma;
	}
	ll ans=f[m][1];
	for(ll j=2;j<=p;j++)
	{
		l=r=1;
		q[r]=0;
		for(ll i=1;i<=m;i++)
		{
//			while(l<r&&((f[q[l]][j-1]+a[q[l]].Suma)-(f[q[l+1]][j-1]+a[q[l+1]].Suma))<=a[i].a*(q[l]-q[l+1]))l++;
			while(l<r&&f[q[l+1]][j-1]+a[q[l+1]].Suma-a[i].a*q[l+1]<f[q[l]][j-1]+a[q[l]].Suma-a[i].a*q[l])l++;
//			cout<<l<<' ';
//			cout<<j<<' '<<q[l]<<endl;
			f[i][j]=f[q[l]][j-1] +a[q[l]].Suma -a[i].Suma +a[i].a*(i-q[l]);
			while(l<r&&(ry(q[r],j)-ry(i,j))*(q[r-1]-i)<=(ry(q[r-1],j)-ry(i,j))*(q[r]-i))r--;
			q[++r]=i;
		}
		ans=min(ans,f[m][j]);	
	}
	cout<<ans<<endl;
	return 0;
}

这题。。
我推的式子,5步错了三次
全是符号错了,最后写的时候还把大于小于打反了
我真的是服了
焯!