Codeforces 85B. Embassy Queue【线段树、贪心】

题目大意:

每个人要办理签证都必须到大使馆中办理三种手续,而且这三种手续办的顺序是固定的。给出办理每种手续的窗口有多少个,办理每个手续花费的时间,有多少人会来办理手续,一级每个人会在什么时间来。要求的是所有人分别在大使馆待的最长时间是多少。

做法:

一种贪心的做法,由于只有三种手续,那么每个人进入办理的时候,保证选办理同种手续的所有窗口中最早等待的那一个窗口(为什么会有等待?因为该窗口办完了上一个,而下一个没能马上到这一个窗口)。接下来对每个人分开处理,计算出每个人从进入大使馆到办完三种手续离开大使馆需要多少时间,最后去最大的一个就行。那么关键点就是怎么计算出这个时间。

前面已经提到,只需要去每种窗口最早等待的那一个窗口即可,那么这样的话,我们需要知道同种类型的窗口的处于等待的时间的最小值,并且修改这个最小值。涉及到这样的操作的话,那当然想到了线段树。我们用三个线段树,分别处理不同类型的窗口的信息。

那有人会想,窗口的个数不是多达10^9个吗?怎么建立这样的线段树?其实这只是个障眼法,总人数最多只能由10^5个,那么最多会用到的窗口最多也就10^5个而已,我们建立树的时候只需要建立窗口数和人数的较小值即可。那这样最终复杂度为O(n*logn)。

代码:

#include <iostream>
#include <cstdio>
#include <climits>
#include <algorithm>
#define N 100010
#define L(x) (x)<<1
#define R(x) (x)<<1|1
using namespace std;
struct node
{
    long long ll,rr,mi,pos;
    node()
    {
        ll=rr=pos=0;
        mi=INT_MAX;
    }
    node(long long l,long long r,long long m,long long p)
    {
        ll=l,rr=r,mi=m,pos=p;
    }
}ty[4][N*4];
long long k[4][N],n[4],t[4],peo[N];
long long nn;
void bulid(long long id,long long l,long long r,long long na)
{
    ty[na][id]=node(l,r,0,l);
    if(l==r) return ;
    long long mid=(l+r)/2;
    bulid(L(id),l,mid,na);
    bulid(R(id),mid+1,r,na);
}
void update(long long id,long long k,long long a,long long na)
{
    if(k==ty[na][id].ll && ty[na][id].rr==k){ty[na][id].mi=a;return ;}
    long long mid=(ty[na][id].ll+ty[na][id].rr)/2;
    if(k>mid) update(R(id),k,a,na);
    else update(L(id),k,a,na);
    if(ty[na][L(id)].mi<=ty[na][R(id)].mi)
    {
        ty[na][id].mi=ty[na][L(id)].mi;
        ty[na][id].pos=ty[na][L(id)].pos;
    }
    else
    {
        ty[na][id].mi=ty[na][R(id)].mi;
        ty[na][id].pos=ty[na][R(id)].pos;
    }
}
long long query(long long id,long long l,long long r,long long na,long long &index)
{
    if(ty[na][id].ll==l&&ty[na][id].rr==r)
    {
        index=ty[na][id].pos;
        return ty[na][id].mi;
    }
    long long mid=(ty[na][id].ll+ty[na][id].rr)/2;
    if(mid>=r) return query(L(id),l,r,na,index);
    else if(mid<l) return query(R(id),l,r,na,index);
    else
    {
        long long tmp1,tmp2;
        long long t1=query(L(id),l,mid,na,tmp1);
        long long t2=query(R(id),mid+1,r,na,tmp2);
        if(t1<=t2) {index=tmp1;return t1;}
        else {index=tmp2;return t2;}
    }
}
int main()
{
    for(long long i=1;i<=3;i++)
        scanf("%I64d",&n[i]);
    for(long long i=1;i<=3;i++)
        scanf("%I64d",&t[i]);
    scanf("%d",&n[0]);
    for(long long i=0;i<n[0];i++)
        scanf("%I64d",peo+i),peo[i]--;

    for(long long i=1;i<=3;i++)
        bulid(1,1,min(n[i],n[0]),i);
    long long ans=0;
    for(long long i=0;i<n[0];i++)
    {
        long long cur=peo[i];
        for(long long j=1;j<=3;j++)
        {
            long long index;
            long long c_mi=query(1,1,min(n[j],n[0]),j,index);
            if(c_mi>=cur) cur=c_mi+t[j];
            else cur+=t[j];
            update(1,index,cur,j);
        }
        ans=max(ans,cur-peo[i]);
    }
    cout<<ans<<endl;
    return 0;
}
时间: 2024-10-12 19:20:49

Codeforces 85B. Embassy Queue【线段树、贪心】的相关文章

[Codeforces 1295E]Permutation Separation(线段树+贪心)

[Codeforces 1295E]Permutation Separation(线段树+贪心) 题面 给出一个排列\(p_1,p_2,...p_n\).初始时你需要选择一个位置把排列分成左右两个.然后在两个序列间移动元素使得左边序列的所有元素都比右边的所有元素小.给出每个元素\(p_i\)从一个序列移动到另一个序列的代价\(a_i\). 分析 显然最后得到的序列是小的数在一边,大的数在另一边.设从值为\(i\)的元素处分开之后移动代价为\(ans_i\). 一开始假设所有数都移到右边序列,那么

Codeforces 46D Parking Lot(线段树)

题目链接:Codeforces 46D Parking Lot 题目大意:一个街道,长为N,每辆车停进来的时候必须和前面间隔B米,和后面间隔F米,现在用两种操作,1是停进来一个长为x的车,2是第x辆车开走. 解题思路:区间合并,建一颗长度为N + B + F的线段树,然后每次停车进去的时候都查询x + B + F的区间,然后修改的时候只修改x的长度. #include <cstdio> #include <cstring> #include <algorithm> us

【Luogu】P1607庙会班车Fair Shuttle(线段树+贪心)

我不会做贪心题啊--贪心题啊--题啊--啊-- 我真TM菜爆了啊-- 这题就像凌乱的yyy一样,把终点排序,终点相同的按起点排序.然后维护一个查询最大值的线段树.对于一个区间[l,r],如果这个区间已经有的最大值为s,那么这个区间最多还能装下c-s头奶牛. 当然奶牛数量没那么多的话我也是没有办法 最后说一句,奶牛到终点就下车了,可以给别的奶牛腾空间,不计入个数.所以奶牛在车上的区间为[l,r-1]. #include <cstdio> #include <iostream> #in

HDU 5638 Toposort 线段树+贪心

题意:bc round 74 分析: 参考下普通的用堆维护求字典序最小拓扑序, 用某种数据结构维护入度小于等于k的所有点, 每次找出编号最小的, 并相应的减少k即可. 这个数据结构可以用线段树, 建立一个线段树每个节点[l,r]维护编号从ll到rr的所有节点的最小入度, 查询的时候只需要在线段树上二分, 找到最小的x满足入度小于等于k. 复杂度O((n+m)logn) #include <iostream> #include <cstdio> #include <vector

codeforces 522D. Closest Equals 线段树+离线

题目链接 n个数m个询问, 每次询问输出给定区间中任意两个相同的数的最近距离. 先将询问读进来, 然后按r从小到大排序, 将n个数按顺序插入, 并用map统计之前是否出现过, 如果出现过, 就更新线段树. 如果当前的i等于某个询问的r, 那么就查询, 具体看代码. 1 #include <iostream> 2 #include <vector> 3 #include <cstdio> 4 #include <cstring> 5 #include <

Codeforces 482B Interesting Array(线段树)

题目链接:Codeforces 482B Interesting Array 题目大意:给定一个长度为N的数组,现在有M个限制,每个限制有l,r,q,表示从a[l]~a[r]取且后的数一定为q,问是 否有满足的数列. 解题思路:线段树维护,每条限制等于是对l~r之间的数或上q(取且的性质,相应二进制位一定为1),那么处理完所有的 限制,在进行查询,查询对应每个l~r之间的数取且是否还等于q.所以用线段树维护取且和,修改为或操作. #include <cstdio> #include <c

CodeForces 19D Points(线段树+map)

开始想不通,后来看网上说是set,就有一个想法是对每个x建一个set...然后又想直接建立两重的set就好,最后发现不行,自己想多了...  题意是给你三种操作:add (x y) 平面添加(x y)这个点 remove (x y)平面删除(x y)这个点 find (x y) 查找(x y)这个点严格的右上方中最左边的点,有多个就再找最下方的点,输出 其实想通了还是比较简单的,我的想法就是对于x先排序再对y排序,这样建一颗线段树,用处在于:添加和删除都可以当成单点更新,只需要记录最大值就好.f

Codeforces 755D(思维+线段树)

http://codeforces.com/problemset/problem/755/D 从X到X+k点,其实只要求从X到X+k之间的点有多少条线超过X--X+K这条线就行,一开始直接暴力,就时间超时了,而用线段树维护就快多了. 1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 #define N 1000010 5 #define INF 0x3f3f3f3f 6 #define lso

Codeforces 487B. Strip DP+线段树+二分

dp[ i ]表示到第i个位置最少要分多少下, dp[ i ] = min ( dp [ i ] , dp [ j ] + 1 ) j 在合适的范围内 (  满足长度和最值差 ) 对整个数组建立线段树维护最大值和最小值这样就可在nlogn的时间里求出某一段的最值差,这个范围是满足单调性的,所以对于每个i可以二分出j的最小值 . 对每个dp[i]建立线段树,可以在nlogn时间内求出最小的j. 所以总时间复杂度n^2logn B. Strip time limit per test 1 secon