// 仅以此题,表示自己的成长之路
// 多校的一道题目,
// 题意在一个圆上有n棵苹果树,每个苹果树上有a[i]个苹果
// 从起点0出发,告诉你圆的周长L,你有一个篮子,容量为k
//求从起点0出发将所有苹果树 上的苹果都搬到0处所花费的最小的距离。
// 解题思路
// 最开始我想到的是训练指南上的捡垃圾的dp那道题目,
// 不过一个是一维轴,一个是二维坐标,就类比了一下
// 怪自己技术太搓了,折腾了三个多小时,最后才在学长的
// 帮助之下过了这道题目
// 我用的是dp的思路
// 先将所有的苹果树拆开成只有一个苹果的cnt棵树
// dp[i]表示捡完前i棵苹果所走的最小的距离
// 很容易想到dp[i] = min(dp[j] + cost) 1<= j <= i;
// 这个cost就是j后面的第j+1的点到0的距离和它到第i个点的距离
// 再加上第i个点回到0的距离
// 因为是一个圆,可以顺时针,也可以逆时针走
// 再处理一个前缀和
// dp[i] = min(dp[j] - total_dist[j+1] + min(dist[j+1],((L-dist[j+1])+L)%L))
// + total_dist[i] + min(dist[i],(L-dist[i] + L)%L);
// 即维护一个func(j)最小值,我用的是单调队列优化。
// 堆优化的话应该也可以做,不过应该会慢一些。
// 说实在的,其实沉下心来钻研一道题目还是很有收获的,
// 心是静的,继续加油吧,题解说可以贪心做,小子实在是
// 无法想到,也不会,如果有大牛有任何想法,希望可以不吝惜
// 地教教小子,小子一定不胜感激
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 1000;
ll d[maxn*2];
int n,L,k;
int cnt;
struct node {
int pos;
int val;
};
node a[maxn*2];
bool cmp(node a,node b){
return a.pos<b.pos;
}
ll total_dist[maxn*2],total_weight[maxn*2];
ll dist[maxn*2];
ll deq[maxn*2];
void print(){
for (int i=1;i<cnt;i++){
printf("%d %d %d %d\n",total_dist[i],total_weight[i],dist[i],d[i]);
}
}
void out(){
for (int i=1;i<cnt;i++){
printf("%d %d %d\n",i,a[i].pos,a[i].val);
}
}
void input(){
memset(d,0,sizeof(d));
memset(deq,0,sizeof(deq));
memset(total_dist,0,sizeof(total_dist));
memset(total_weight,0,sizeof(total_weight));
memset(dist,0,sizeof(dist));
memset(a,0,sizeof(a));
scanf("%d%d%d",&L,&n,&k);
cnt = 1;
int u,v;
for (int i=0;i<n;i++){
scanf("%d%d",&u,&v);
if (u==0 || u == L)
continue;
while(v>0){
a[cnt].pos = u;
a[cnt++].val = 1;
v--;
}
}
//out();
// if (cnt==1){
// printf("0\n");
// return ;
// }
sort(a+1,a+cnt,cmp);
total_weight[0] = 0;
total_dist[0] = 0;
for (int i=1;i<cnt;i++){
total_weight[i] = total_weight[i-1] + a[i].val;
total_dist[i] = total_dist[i-1] + a[i].pos - a[i-1].pos;
dist[i] = a[i].pos;
}
}
ll func(int i){
return d[i] - total_dist[i+1] + min(dist[i+1],((L-dist[i+1])+L)%L);
}
void solve(){
int head,tail;
head = tail = 1;
for (int i=1;i<cnt;i++){
while(tail>=head && total_weight[i] - total_weight[deq[head]] > k)
head++;
d[i] = func(deq[head]) + total_dist[i] + min(dist[i],(L-dist[i] + L)%L);
//printf("i = %d %d %d func[deq[%d]] = %d deq[head]=%d\n",i,deq[head],d[i],head,func(deq[head]),deq[head]);
while(head<=tail && func(i) <= func(deq[tail]))
tail--;
deq[++tail] = i;
}
printf("%I64d\n",d[cnt-1]);
//print();
}
int main(){
int t;
//freopen("1.txt","r",stdin);
scanf("%d",&t);
while(t--){
input();
solve();
}
return 0;
}
2015 Multi-University Training Contest 2 1004 苹果树 dp+单调队列
最新推荐文章于 2022-04-10 01:42:22 发布