正题
第一题:Door Street 是一条繁华的街道,沿街一共有栋大楼,编号依次为
,相邻两栋楼编号相邻。 现在你想在 Door Street 开一家咖啡厅,你可选址在任意一栋楼内。每栋楼都有一个消费指数
。若你 选址在第x号楼,则第i号楼的人在你的咖啡厅消费
,你的收入是 n 栋大楼的 人的消费总和
,即 。 对于每栋大楼,你想评估出你若选址在该大楼,你的收入分别是多少?
发现一个点只对周围的一些点产生贡献,这个范围很容易算。
把贡献展开,就可以发现
。
那么前面这个东西差分维护一下,后面的i差分维护一下,个数差分维护一下就做完了。
不用学习博主的做法,因为这个树状数组是废的。
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#define lowbit(x) (x&(-x))
using namespace std;
int n;
long long c[100010];
long long sum[100010],s[100010],num[100010];
void add(long long*a,int x,long long t){
while(x<=n){
a[x]+=t;
x+=lowbit(x);
}
}
long long get_sum(long long*a,int x){
long long tot=0;
while(x){
tot+=a[x];
x-=lowbit(x);
}
return tot;
}
int main(){
freopen("addr.in","r",stdin);
freopen("addr.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lld",&c[i]);
int tot,l,r;
for(int i=1;i<=n;i++){
tot=sqrt(c[i]);
if(i-tot>=1) l=i-tot;else l=1;
if(i+tot<=n) r=i+tot;else r=n;
add(sum,l,c[i]-1ll*i*i);add(sum,r+1,1ll*i*i-c[i]);
add(s,l,2*i);add(s,r+1,-2*i);
add(num,l,1);add(num,r+1,-1);
}
for(int i=1;i<=n;i++)
printf("%lld ",get_sum(sum,i)+i*(get_sum(s,i)-get_sum(num,i)*i));
}
第二题:某地区的地图可以看成是一个 n 个点 m 条带权边的无向图,其中有 k 个点是加油站,而汽车在加油站可以把油箱加满。已知 x 单位的油箱 在不加油的情况下最多能走 x 单位的路程。 现在给定 q 组询问,每次询问能否开一辆油箱容量为 v 单位的汽车从 点 x 走到点 y。 k, n, q ≤ 200000,保证所有点 x、y 均为加油站。
题目就叫随堂测试,因为这题前几天的课上讲过,题解可以直接看我之前的blog。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
int n,m,k,q;
struct edge{
int x,y,next,c;
bool operator<(const edge q){
return c<q.c;
}
}s[800010],now[400010],e[800010];
int first[200010],fir[200010],len;
int p[200010],dis[200010],from[200010],dep[200010];
int f[200010][20],g[200010][20];
int fa[200010];
bool tf[200010];
queue<int> list;
int findpa(int x){
if(fa[x]!=x) return fa[x]=findpa(fa[x]);
return x;
}
void ins(edge*s,int*first,int x,int y,int c){
s[++len]=(edge){x,y,first[x],c};first[x]=len;
s[++len]=(edge){y,x,first[y],c};first[y]=len;
}
void SPFA(){
memset(dis,63,sizeof(dis));
for(int i=1;i<=k;i++) list.push(p[i]),tf[p[i]]=true,dis[p[i]]=0,from[p[i]]=p[i];
while(!list.empty()){
int x=list.front();list.pop();tf[x]=false;
for(int i=first[x];i!=0;i=s[i].next){
int y=s[i].y;
if(dis[y]>dis[x]+s[i].c){
dis[y]=dis[x]+s[i].c;
from[y]=from[x];
if(!tf[y]) tf[y]=true,list.push(y);
}
}
}
}
void dfs(edge*s,int*first,int x,int fa){
for(int i=first[x];i!=0;i=s[i].next){
int y=s[i].y;
if(y==fa) continue;
f[y][0]=x;g[y][0]=s[i].c;dep[y]=dep[x]+1;
for(int k=1;k<=19;k++)
f[y][k]=f[f[y][k-1]][k-1],g[y][k]=max(g[y][k-1],g[f[y][k-1]][k-1]);
dfs(s,first,y,x);
}
}
int get_max(int x,int y){
int ans=0;
if(dep[y]<dep[x]) swap(x,y);
for(int k=19;k>=0;k--) if(dep[f[y][k]]>=dep[x])
ans=max(ans,g[y][k]),y=f[y][k];
if(x==y) return ans;
for(int k=19;k>=0;k--) if(f[y][k]!=f[x][k]){
ans=max(ans,max(g[x][k],g[y][k]));
x=f[x][k];y=f[y][k];
}
ans=max(ans,max(g[x][0],g[y][0]));
return ans;
}
int main(){
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
scanf("%d %d",&n,&m);
int x,y,c,t=0;
for(int i=1;i<=m;i++) scanf("%d %d %d",&x,&y,&c),ins(s,first,x,y,c);
scanf("%d",&k);for(int i=1;i<=k;i++) scanf("%d",&p[i]);
SPFA();
for(int i=1;i<=len;i+=2) if(from[s[i].x]!=from[s[i].y])
now[++t]=(edge){from[s[i].x],from[s[i].y],0,dis[s[i].x]+dis[s[i].y]+s[i].c};
sort(now+1,now+1+t);
for(int i=1;i<=n;i++) fa[i]=i;len=0;
for(int i=1;i<=t;i++){
int fx=findpa(now[i].x),fy=findpa(now[i].y);
if(fx!=fy) ins(e,fir,now[i].x,now[i].y,now[i].c),fa[fx]=fy;
}
dep[p[1]]=1;dfs(e,fir,p[1],0);scanf("%d",&q);
while(q--){
scanf("%d %d",&x,&y);
printf("%d\n",get_max(x,y));
}
}
第三题:建筑需要整体结构来维持,不能只靠地基…… —— LazyJazz [万圣节Party进行时……] LazyJazz号召大家一起来搭积木,搭一座巨高巨高的积木塔。大家一起先构思了一个最终目标,即最终 塔的形状,然后打算分工搭出若干小积木塔,每个小积木塔对应最终塔的某一段结构,最后一个一个摞 起来。 积木塔的最终形态由 块密度均匀且相等,宽、高相等,长度不定的积木上下摞在一起,宽对齐,从上 至下数第
块积木覆盖了横坐标对应
至
的范围(包含
,
两点),长度为
单位长 度。根据前面的描述,可以得出结论,每块积木的质量正比于其长度。 由于是积木塔,所以塔有可能不稳定,稳定的判定条件是:若一个积木塔有
层,当且仅当对于任意 ,从上至下数,前
块积木的重心落在第
块积木的覆盖范围内,则稳定,否则不稳 定。前
块积木的重心为前
块积木的几何中心,以质量为权重的带权平均位置。
因为对于一个i满足条件,那么以i为结尾的所有子段都满足条件,所以我们要求的就是i到底端是否满足条件。
可以直接过一遍获得80分的好成绩,正解:考虑取消自顶向下前i个积木个贡献,对下面的每一个积木是否满足都会有影响,这个影响是可以算出来的,最后上面的改变对于当前积木的判定是一个二元一次不等式的形式,可以直接将这条直线画在李超线段树上,维护即可。
具体可以看http://lazyjazz.blog.zijian-lv.com/blog/13
一开始没想到上面对下面的影响,是因为我的物理不好,不能简洁的算出i个积木的中心。所以就化简不了。
代码就先不写了,贴一个考场80分的。先去学半平面交。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
int n;
double l[100010],r[100010],pos[100010],len[100010];
int f[100010];
const double eps=1e-7;
int main(){
//freopen("bricks.in","r",stdin);
// freopen("bricks.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++) f[i]=1e9;
for(int i=1;i<=n;i++){
scanf("%lf %lf",&l[i],&r[i]);
pos[i]=(l[i]+r[i])/2,len[i]=r[i]-l[i];
}
double tpos=0,tlen=0;
for(int i=n;i>=1;i--){
tpos=pos[i],tlen=len[i];
bool we=true;
for(int j=i+1;j<=n;j++){
if(tpos+eps<l[j] || r[j]+eps<tpos) {we=false;break;}
if(f[j]!=1e9) f[i]=min(f[i],max(f[j],j-i));
tpos+=(pos[j]-tpos)*len[j]/(tlen+len[j]);
tlen+=len[j];
}
if(we==false) f[i]=1e9;
else f[i]=min(f[i],n+1-i);
}
printf("%d\n",f[1]);
}

本文精选三道算法竞赛题目,包括选址开店问题、路径可达性判断及积木塔稳定性分析,详细解析了每题的算法思路与代码实现,是算法学习者不可多得的参考资料。
3448

被折叠的 条评论
为什么被折叠?



