P8632 [蓝桥杯 2015 国 B] 居民集会 题解
toolong114514 · · 题解
P8632 [蓝桥杯 2015 国 B] 居民集会 题解
题面大意
传送门。
解题思路
非常典的斜率优化。
居民们肯定是去离他们最近,且可以到达的集会点,否则一定不优。
根据题面的
记
可得以下的状态转移方程:
按上述状态转移方程搞一个暴力转移,时间复杂度为
考虑对上式进行斜率优化。
变换式子,得:
记
去掉括号,写成斜优的标准形式如下:
令
使用李超线段树(LCT)维护即可。
最后的答案即为
编写代码时,可以让
参考代码
#include<algorithm>
#include<iostream>
using namespace std;
#define s sum1
#define int long long
const int N=1e6+10;
const int INF=0x7fffffff;
struct node{
int l,r,bst;
}tree[4*N];
struct ccf{
int k,b;
}ln[N];
void build(int pos,int lft,int rgt){
tree[pos].l=lft;
tree[pos].r=rgt;
if(lft==rgt) return;
int mid=(lft+rgt)/2;
build(pos*2,lft,mid);
build(pos*2+1,mid+1,rgt);
}
void upd(int pos,int num){
int mid=(tree[pos].l+tree[pos].r)/2;
if((ln[tree[pos].bst].k==ln[num].k)&&(ln[tree[pos].bst].b==ln[num].b)) return;
if(ln[tree[pos].bst].k*mid+ln[tree[pos].bst].b>ln[num].k*mid+ln[num].b) swap(tree[pos].bst,num);
if(tree[pos].l==tree[pos].r) return;
if(ln[num].k*tree[pos].l+ln[num].b<ln[tree[pos].bst].k*tree[pos].l+ln[tree[pos].bst].b) upd(pos*2,num);
if(ln[num].k*tree[pos].r+ln[num].b<ln[tree[pos].bst].k*tree[pos].r+ln[tree[pos].bst].b) upd(pos*2+1,num);
}
int ask(int pos,int kk){
if(kk<tree[pos].l||tree[pos].r<kk) return INF;
if(tree[pos].l==tree[pos].r) return ln[tree[pos].bst].k*kk+ln[tree[pos].bst].b;
return min(min(ask(pos*2,kk),ask(pos*2+1,kk)),ln[tree[pos].bst].k*kk+ln[tree[pos].bst].b);
}
int n,l,cnt;
int sum1[N],sum2[N],f[N];
signed main(){
ios::sync_with_stdio(false);
cin>>n>>l;
while(n--){
int dd,tt;
cin>>dd>>tt;
s[dd]+=tt;
}
for(int i=1;i<=l;i++){
sum2[i]=sum2[i-1]+s[i]*i;
s[i]+=s[i-1];
}
ln[0].b=INF;
build(1,0,l);
ln[++cnt]={0,0};
upd(1,cnt);
for(int i=1;i<=3;i++){
for(int j=0;j<=l;j++){
f[j]=ask(1,j)+sum1[j]*j-sum2[j];
}
build(1,0,l);
cnt=0;
for(int j=0;j<=l;j++){
ln[++cnt]={-sum1[j],sum2[j]+f[j]};
upd(1,cnt);
}
}
cout<<ask(1,l)+sum1[l]*l-sum2[l];
return 0;
}
此代码可以通过洛谷评测。
Written by toolong114514 on 2024/6/12.