题解:P11118 [ROI 2024] 无人机比赛 (Day 2)
Meta_Morph · · 题解
P11118 [ROI 2024] 无人机比赛 (Day 2)
博客园链接:P11118 [ROI 2024] 无人机比赛 (Day 2) - Add_Catalyst - 博客园 (cnblogs.com)。
知识点
分块。
分析
首先有一个显而易见的贪心:如果一个无人机飞了一段路,而下一段路的长度小于等于这段路,那么它肯定会继续飞。证明也显然。
但是并不那么显而易见的是它的作用:注意到
根据上述贪心,我们按
那么接下来就简单了,我们先按
在排序后的序列中,如果
-
-
$$ <d_x\times t_i,idx_i> \ \le \ <d_{tot}\times t_j,idx_j>\\ $$ 那么贡献就是 $\sum_{i=1}^{x_m} c_i$。
现在考虑加入题目的限制,维护一个一个插入的总贡献。
首先可以对每个点
然后按原序列顺序遍历
-
我们考虑满足
pos_j<pos_i\land j<i 的j 对i 的贡献:遍历
x(1\le x\le tot) ,那么这一段的贡献部分就是c_i\sum_{j} [pos_j\le pre_{i,x}] 。那么可以用数据结构实现
O(n) 次修改和O(n\sqrt{s_m}) 次查询。考虑使用O(\sqrt{n}) 修改、O(1) 查询的分块来平衡复杂度。 -
我们考虑
i 对满足pos_j>pos_i\land j<i 的j 的贡献:遍历
x(1\le x\le tot) ,那么这一段的贡献部分就是c_i\sum_{j} [pos_i\le pre_{j,x}] 。那么可以用数据结构实现
O(n\sqrt{s_m}) 次修改和O(n) 次查询。考虑使用O(1) 修改、O(\sqrt{n}) 查询的分块来平衡复杂度。
那么最后前缀累加,记为
代码
#define Plus_Cat ""
#include<bits/stdc++.h>
#define ll long long
#define Pli pair<ll,int>
#define FOR(i,a,b) for(int i(a);i<=(int)(b);++i)
#define DOR(i,a,b) for(int i(a);i>=(int)(b);--i)
#define main Main();signed main(){ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);return Main();}signed Main
using namespace std;
constexpr int N(1.5e5+10),M(547+10);
namespace IOcat {
#define LIM (1<<22)
char buf[LIM],*ip;
void In(int &x) {
x=0;
static char ch(0);
while(ch=*ip++,ch<48);
do x=(x<<1)+(x<<3)+(ch^48);
while(ch=*ip++,ch>47);
}
char pbuf[LIM],*pp;
void Out(ll x) {
static int top(0);
static char st[50];
do st[++top]=x%10^48,x/=10;
while(x);
while(top)*pp++=st[top--];
*pp++='\n';
}
} using namespace IOcat;
int n,m,tot;
int t[N],s[M],c[M],idx[N],pos[N];
int pre[N][M];
ll ans;
namespace Divide_Block {
constexpr int BL(388+10),BN(N/(BL-10)+10);
int Bl,Bn;
int st[BN],en[BN],bel[N];
/*1.Change:O(B),Query:Q(1)*/
int v1[N],s1[BN];
void Plus1(int x) {
FOR(i,x,en[bel[x]])++v1[i];
FOR(i,bel[x],Bn)++s1[i];
}
int Sum1(int x) { return v1[x]+s1[bel[x]-1]; }
/*2.Change:O(1),Query:Q(B)*/
ll v2[N],s2[BN];
void Plus2(int x,ll d) { v2[x]+=d,s2[bel[x]]+=d; }
ll Sum2(int x) {
ll ans(0);
FOR(i,x,en[bel[x]])ans+=v2[i];
FOR(i,bel[x]+1,Bn)ans+=s2[i];
return ans;
}
/*Build*/
void Build(const int n) {
Bl=ceil(sqrt(n)),Bn=(n-1)/Bl+1;
FOR(i,1,Bn) {
st[i]=en[i-1]+1,en[i]=min(en[i-1]+Bl,n);
FOR(j,st[i],en[i])bel[j]=i;
}
}
} using namespace Divide_Block;
signed main() {
/*DE("Input");*/
fread(buf,1,LIM,stdin),ip=buf,pp=pbuf;
//n,m
In(n),In(m);
//t
FOR(i,1,n)In(t[i]),idx[i]=i;
sort(idx+1,idx+n+1,[&](const int &a,const int &b) { return t[a]^t[b]?t[a]<t[b]:a<b; });
FOR(i,1,n)pos[idx[i]]=i;
//s
int lst(0);
FOR(i,1,m) {
int cur;
In(cur);
if(!tot||cur-lst>s[tot])s[++tot]=cur-lst;
++c[tot],lst=cur;
}
/*DE("Init");*/
Build(n);
FOR(i,1,tot) {
int it(0);
FOR(j,1,n) {
const int &u(idx[j]);
while(it<n&&Pli((ll)t[idx[it+1]]*s[i],idx[it+1])<=Pli((ll)t[u]*s[tot],u))++it;
pre[u][i]=it;
}
}
/*DE("Solve & Output");*/
FOR(i,1,n) {
//small ones to it
FOR(j,1,tot)ans+=(ll)Sum1(pre[i][j])*c[j];
Plus1(pos[i]);
//it to big ones
FOR(j,1,tot)Plus2(pre[i][j],c[j]);
ans+=Sum2(pos[i]);
//output
Out(ans-=m);
}
fwrite(pbuf,1,pp-pbuf,stdout);
return 0;
}