T1-Lifeguards
FJ为他的奶牛们建造了一个游泳池,FJ认为这将有助于他们放松身心以及生产更多牛奶。
为了确保奶牛们的安全,FJ雇佣了N头牛,作为泳池的救生员,每一个救生员在一天内都会有一定的事情,并且这些事情都会覆盖一天内的一段时间。为了简单起见,泳池从时间t=0时开门,直到时间t=1000000000关门,所以每个事情都可以用两个整数来描述,给出奶牛救生员开始以及结束事情的时间。例如,一个救生员在时间t=4时开始事情并且在时间t=7时结束事情,那么这件事情就覆盖了3个单位时间。(注意:结束时间是“点”的时间)
不幸的是,FJ多雇佣了一名的救生员,但他没有足够的资金来雇佣这些救生员。因此他必须解雇一名救生员,求可以覆盖剩余救生员的轮班时间的最大总量是多少?如果当时至少有一名救生员的事情已经开始,则这个时段被覆盖。
解法
因为只要解雇一名救生员,一个基础的思想是求出每个救生员单独工作的时间,总时间减去最小单独工作时间就是答案
用线性的方式从左到右扫一遍,中途求解并更新答案
ac代码
#include <bits/stdc++.h>
using namespace std;
int n,ans,sr,res=100000000,rr,s;
struct p{int l,r;}a[100010];
int cmp(p x,p y){return x.l<y.l;}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d%d",&a[i].l,&a[i].r);
sort(a+1,a+n+1,cmp);//按照左端点排序
for(int i=1;i<=n;i++)if(a[i].r>sr)rr=max(sr,a[i].l),ans+=a[i].r-rr,sr=a[i].r;
//如果原来记录的覆盖点的末端小于当前工作结束点,加入新覆盖线段的长度并更新覆盖线段的末端
sr=0,a[n+1].l=a[n].r;
//重新初始化并建立一个边界
for(int i=1;i<=n;i++)
if(a[i].r<=sr)res=0;
else s=min(a[i+1].l,a[i].r)-max(a[i].l,sr),res=min(res,s),sr=max(a[i].r,sr);
//求出最小的一段并不断缩小查找范围
res=max(0,res),printf("%d",ans-res);
return 0;
}
T2-Rental Service
farmer john有N(1≤N≤100,000)头牛,他想赚跟多的钱,所以他准备买牛奶和出租牛。有M(1≤M≤100,000)家商店想买牛奶,每家商店的进货价不同。有R(1≤R≤100,000)户邻居想租牛,每户人家的租价不同。 问他最多能赚多少钱。
解法
这道题很容易看出来是贪心了,把产奶少的租出去,产奶多的卖奶,排个序枚举就可以
ac代码
#include <bits/stdc++.h>
using namespace std;
struct node{long long q,p;}a[100010];
long long n,m,k,c[100010],r[100010],res=0,ans,nw=1,s=0;
bool cmp(node x,node y){return x.p>y.p;}
bool cmpx(int x,int y){return x>y;}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(long long i=1;i<=n;i++)scanf("%d",&c[i]);
for(long long i=1;i<=m;i++)scanf("%d%d",&a[i].q,&a[i].p);
for(long long i=1;i<=k;i++)scanf("%d",&r[i]);
sort(c+1,c+1+n),sort(a+1,a+1+m,cmp),sort(r+1,r+1+k,cmpx);
for(long long i=1;i<=min(n,k);i++)res+=r[i];
for(long long i=k+1;i<=n;i++)s+=c[i];
while(s!=0&&nw<=m)
if(s>=a[nw].q)res+=a[nw].q*a[nw].p,s-=a[nw].q,nw++;
else res+=s*a[nw].p,a[nw].q-=s,s=0;
ans=res;
//初始为能租就租,剩下的卖奶
for(long long i=min(n,k);i>=1;i--)
{
res-=r[i],s=c[i];
while(s!=0&&nw<=m)
if(s>=a[nw].q)res+=a[nw].q*a[nw].p,s-=a[nw].q,nw++;
else res+=s*a[nw].p,a[nw].q-=s,s=0;
ans=max(ans,res);
}
//每次将一头租的奶牛换为卖奶,更新答案
printf("%lld",ans);
return 0;
}
T3-MooTube
在业余时间,Farmer John创建了一个新的视频共享服务,他将其命名为MooTube。在MooTube上,Farmer John的奶牛可以录制,分享和发现许多有趣的视频。他的奶牛已经发布了 NN 个视频 (1≤N≤100,000),为了方便将其编号为1…N。然而,FJ无法弄清楚如何帮助他的奶牛找到他们可能喜欢的新视频。
FJ希望为每个MooTube视频创建一个“推荐视频”列表。这样,奶牛将被推荐与他们已经观看过的视频最相关的视频。
FJ设计了一个“相关性”度量标准,顾名思义,它确定了两个视频相互之间的相关性。他选择N-1对视频并手动计算其之间的相关性。然后,FJ将他的视频建成一棵树,其中每个视频是节点,并且他手动将N−1对视频连接。为了方便,FJ选择了N−1对,这样任意视频都可以通过一条连通路径到达任意其他视频。FJ决定将任意一对视频的相关性定义为沿此路径的任何连接的最小相关性。
Farmer John想要选择一个K值,以便在任何给定的MooTube视频旁边,推荐所有其他与该视频至少有K相关的视频。然而,FJ担心会向他的奶牛推荐太多的视频,这可能会分散他们对产奶的注意力!因此,他想设定适当的K值。Farmer John希望得到您的帮助,回答有关K值的推荐视频的一些问题。
解法
这道题要用一个离线算法,用连通块的方式,只是连通条件为当前的边长大于K,将边和K值都排序,顺序枚举找连通块
ac代码
#include<bits/stdc++.h>
using namespace std;
int n,q,fa[100005],siz[100005],res[100005];
struct edge{int p,q,r;}a[100005];
struct ask{int k,v,num;}b[100005];
int cmp(edge x,edge y){return x.r>y.r;}
int cmpx(ask x,ask y){return x.k>y.k;}
int find(int u){if(fa[u]!=u)fa[u]=find(fa[u]);return fa[u];}
int main()
{
scanf("%d%d",&n,&q);
for(int i=1;i<n;i++)scanf("%d%d%d",&a[i].p,&a[i].q,&a[i].r);
for(int i=1;i<=q;i++)scanf("%d%d",&b[i].k,&b[i].v),b[i].num=i;
sort(a+1,a+n+1,cmp),sort(b+1,b+q+1,cmpx);
//将边和询问都排序
for(int i=1;i<=n;i++)fa[i]=i,siz[i]=1;
int j=1,x,y;
for(int i=1;i<=q;i++)
{
while(a[j].r>=b[i].k&&j<n)
{
x=find(a[j].p),y=find(a[j].q),j++;
if(x==y)continue;
fa[x]=y,siz[y]+=siz[x];
}
res[b[i].num]=siz[find(b[i].v)]-1;
}
//很关键的一点是要继承上一个询问的连通块,因为K值是从大到小排的
//在大的K值连通,在小的K值一定连通,复杂度就降低很多了
for(int i=1;i<=q;i++)printf("%d\n",res[i]);
return 0;
}