总差一步。
1833 打地鼠的价值
题意:所有 只地鼠分布在一条直线上,第 个地鼠洞坐标为 ,地鼠只会在 出现,打到后可获得 的价值。你只能通过机械锤来打地鼠,初始可以将它移到任意位置,它的速度有上限 每单位每秒。你只有在 时刻将机械锤移动到 才能打到地鼠并获得价值。请问最大能获得的价值。
解析:首先我们肯定是按 递增的顺序去打地鼠,然后可以很快推出 DP 式子。
设 表示最后打的是第 只地鼠时获得的最大价值,那么
显然第二个条件已经包含了第一个条件。
对于这个条件,将绝对值去掉后可以化为
然后就是一个经典的二维偏序问题了。按照其中一个维度排序后,另一维度用树状数组维护即可。注意要离散化。
代码:
//
// Created by 屋顶上的小丑 on 2023/4/3.
//
#include<cstdio>
#include<cstring>
const int maxn=3e5;
const int mod=1e6+7;
const long long inf=1e8;
int n,v,tot;
long long c[(maxn<<1)+5],f[maxn+5];
long long tr[(maxn<<1)+5];
struct mouse
{
long long val1;
long long val2;
long long w;
}p[maxn+5],tmp[maxn+5];
struct hash_map
{
struct data
{
long long u;
int v;
int nex;
}e[mod+5];
int head[mod+5],cnt;
int hash(long long u) { return u%mod; }
int& operator[](long long u)
{
int fir=hash(u);
for(int i=head[fir];i;i=e[i].nex)
if(e[i].u==u)
return e[i].v;
e[++cnt]={u,-1,head[fir]};
head[fir]=cnt;
return e[cnt].v;
}
hash_map()
{
cnt=0;
memset(head,0,sizeof(head));
}
};
bool cmp(mouse a,mouse b)
{
if(a.val1==b.val1)
return a.val2<b.val2;
return a.val1<b.val1;
}
bool cmp1(mouse a,mouse b)
{
return a.val2<b.val2;
}
int lowbit(int x)
{
return x&(-x);
}
void add(int x,long long val)
{
for(int i=x;i<=tot;i+=lowbit(i))
if(val>tr[i])
tr[i]=val;
}
long long query(int x)
{
long long ans=-inf;
for(int i=x;i;i-=lowbit(i))
if(tr[i]>ans)
ans=tr[i];
return ans;
}
template<class T>
void merge_sort(T x[],T temp[],int l,int r,bool (*comp)(T,T))
{
if(l==r)
return ;
int mid=(l+r)>>1;
merge_sort(x,temp,l,mid,comp);
merge_sort(x,temp,mid+1,r,comp);
int p1=l,p2=mid+1,now=0;
while(p1<=mid&&p2<=r)
{
if(comp(x[p1],x[p2]))
{
temp[++now]=x[p1];
p1++;
}
else
{
temp[++now]=x[p2];
p2++;
}
}
while(p1<=mid)
{
temp[++now]=x[p1];
p1++;
}
while(p2<=r)
{
temp[++now]=x[p2];
p2++;
}
for(int i=l;i<=r;i++)
x[i]=temp[i-l+1];
}
long long dp()
{
f[0]=0;
long long ans=-inf;
for(int i=1;i<=n;i++)
{
f[i]=p[i].w+query(p[i].val2);
add(p[i].val2,f[i]);
if(ans<f[i])
ans=f[i];
}
return ans;
}
hash_map h;
int main()
{
scanf("%d%d",&n,&v);
int x,t;
for(int i=1;i<=n;i++)
{
scanf("%d%d%lld",&x,&t,&p[i].w);
p[i].val1=1ll*v*t+x;
p[i].val2=1ll*v*t-x;
}
merge_sort(p,tmp,1,n,cmp1);
for(int i=1;i<=n;i++)
{
if(~h[p[i].val2])
p[i].val2=h[p[i].val2];
else
{
h[p[i].val2]=++tot;
p[i].val2=tot;
}
}
merge_sort(p,tmp,1,n,cmp);
printf("%lld",dp());
return 0;
}
1829 神秘的机房
题意:给出两个长度为 的数列 ,给出 个询问,每个询问形如 ,求出 。要求强制在线。
解析:这道题强制在线,不能很好用常规方法直接处理,考虑根号分治。
对于某种颜色 ,如果 的点大于 ,那么我们可以直接将颜色 到其他颜色的距离预处理出来。设 表示颜色 到颜色 的距离,显然它要么是点 到左边第一个颜色 的点的距离,或到右边第一个颜色 点的距离。我们分两遍处理,顺着扫一遍再逆着扫一遍,每次即可处理出左边最近的或右边最近的颜色 点的位置,然后即可预处理出 ,在回答时如果 和 中有一个数量大于 即可 回答。由于颜色数量大于 的颜色不会超过 种,所以整个预处理的复杂度为 。
剩下的则是对于 均小于 的情况。我们把这两种颜色的点单独取出,按照 坐标排序,然后用双指针扫一遍求出答案(类似归并),此时排序复杂度为 ,双指针为 。我们可以一开始把所有点的都排好然后再取出即可(或者先拿个数组存下,询问时直接双指针扫)。故这部分的询问复杂度为 。
因此总复杂度为 。
代码:
//
// Created by 屋顶上的小丑 on 2023/4/3.
//
#include<cstdio>
#include<cmath>
const int maxn=1e5;
const int maxb=350;
const int inf=2e8;
int n,m,num[maxn+5];
int num1,num2;//大,小
int id[maxn+5],col[maxb+5];
int dis[maxb+5][maxn+5];
int p[maxn+5][maxb+5];
struct company
{
int x;
int c;
}e[maxn+5],tmp[maxn+5];
bool cmp(company a,company b)
{
return a.x<b.x;
}
template<class T>
void merge_sort(T data[],T temp[],int l,int r,bool (*comp)(T,T))
{
if(l==r)
return ;
int mid=(l+r)>>1;
merge_sort(data,temp,l,mid,comp);
merge_sort(data,temp,mid+1,r,comp);
int p1=l,p2=mid+1,now=0;
while(p1<=mid&&p2<=r)
{
if(comp(data[p1],data[p2]))
{
temp[++now]=data[p1];
p1++;
}
else
{
temp[++now]=data[p2];
p2++;
}
}
while(p1<=mid)
{
temp[++now]=data[p1];
p1++;
}
while(p2<=r)
{
temp[++now]=data[p2];
p2++;
}
for(int i=l;i<=r;i++)
data[i]=temp[i-l+1];
}
int main()
{
scanf("%d%d",&n,&m);
int mid=sqrt(n);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&e[i].x,&e[i].c);
num[e[i].c]++;
}
merge_sort(e,tmp,1,n,cmp);
for(int i=1;i<=n;i++)
{
if(num[e[i].c]>=mid)
{
if(id[e[i].c])
continue;
id[e[i].c]=++num1;
col[num1]=e[i].c;
}
else
{
if(!id[e[i].c])
id[e[i].c]=++num2;
int now=id[e[i].c];
p[now][++p[now][0]]=e[i].x;
}
}
for(int i=1;i<=num1;i++)
{
int now=col[i],las=0;
for(int j=1;j<=n;j++)
dis[i][j]=inf;
for(int j=1;j<=n;j++)
{
if(e[j].c==now)
{
las=e[j].x;
continue;
}
if(las&&e[j].x-las<dis[i][e[j].c])
dis[i][e[j].c]=e[j].x-las;
}
las=0;
for(int j=n;j>=1;j--)
{
if(e[j].c==now)
{
las=e[j].x;
continue;
}
if(las&&las-e[j].x<dis[i][e[j].c])
dis[i][e[j].c]=las-e[j].x;
}
}
int las=0,a,b;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
a^=las;
b^=las;
if((!num[a])||(!num[b]))
{
printf("invalid\n");
las=0;
continue;
}
if(num[a]>=mid)
printf("%d\n",las=dis[id[a]][b]);
else if(num[b]>=mid)
printf("%d\n",las=dis[id[b]][a]);
else
{
a=id[a],b=id[b];
int p1=1,p2=1;
las=inf;
while(p1<=p[a][0]&&p2<=p[b][0])
{
int now=p[b][p2]-p[a][p1];
if(now<0) now=-now;
if(p[a][p1]<=p[b][p2])
{
if(las>now) las=now;
p1++;
}
else
{
if(las>now) las=now;
p2++;
}
}
while(p1<=p[a][0])
{
int now=p[b][p2-1]-p[a][p1];
if(now<0) now=-now;
if(las>now) las=now;
p1++;
}
while(p2<=p[b][0])
{
int now=p[a][p1-1]-p[b][p2];
if(now<0) now=-now;
if(las>now) las=now;
p2++;
}
printf("%d\n",las);
}
}
return 0;
}