Problem A 新婚快乐

一条路,被$n$个红绿灯划分成$n+1$段,从前到后一次给出每一段的长度$l_i$,每走$1$的长度需要$1$分钟。

一开始所有红绿灯都是绿色的,$g$分钟后所有红绿灯变成红色,再过$r$分钟,所有红绿灯又重新变为绿色。

以$r+g$分钟为一个周期,如此反复。

有$Q$组询问,如果第$t_i$分钟从第一条线段的首端出发走到最后一条线段末端需要的时刻。

对于$100\%$的数据满足$1 \leq n\leq 2\times 10^5$ , 其他所有数字都在$10^9$以下。

Solution : 

  设$f[i]$表示第$i$个红绿灯恰好绿灯开始,走到最后的时间长度。

  转移的话就是从最近的一个$j$(即编号最小),且满足$|s_j - s_i| \% (r+g) \geq g$。

  这个时候,我们需要解这样一个带有取模运算的不等式,$(a+b)\% c \geq d$

  我们打一个表发现,若$a=0$,答案显然是$[d,c-1]$,如果对于任意$1 \leq a < c$只要把答案区间往左平移$a$即可。

  这个时候,可能出现段首一部分被覆盖,段尾一部分被覆盖。

  直接用权值线段树维护位置信息即可。

  最后计算答案的时候,也需要解这样一个不等式,类似的,计算出当前出发第一个遇到红灯的位置即可。

  使用动态开点的权值线段树,可以实现时间复杂度$O(n log_2 S)$,其中$S$是值域。

# include <bits/stdc++.h>
# define int long long 
# define root rt
# define inf (1e18)
using namespace std;
const int N=2e5+10;
int n,g,r;
int s[N],f[N];
struct Seg {
    int ls,rs,val;
    Seg() { val = inf; ls=rs=0; }
};
struct QwQ {
    int root,tot;
    QwQ () { root=0;tot=0;}
    Seg tr[N*32];
    void insert(int &x,int l,int r,int pos,int val) {
        if (!x) x=++tot;
        if (l==r) { tr[x].val=val; return;}
        int  mid=(l+r)>>1;
        if (pos<=mid) insert(tr[x].ls,l,mid,pos,val);
        else insert(tr[x].rs,mid+1,r,pos,val);
        tr[x].val=min(tr[tr[x].ls].val,tr[tr[x].rs].val);
    }
    int query(int x,int l,int r,int opl,int opr) {
        if (!x) return inf;
        if (opl<=l && r<=opr) return tr[x].val;
        int ret=inf,mid=(l+r)>>1;
        if (opl<=mid) ret=min(ret,query(tr[x].ls,l,mid,opl,opr));
        if (opr>mid) ret=min(ret,query(tr[x].rs,mid+1,r,opl,opr));
        return ret;
    }
    int query(int x) {
        x=(x%(g+r)+(g+r))%(g+r);
        if (g-x>=0) return query(root,0,g+r-1,g-x,g+r-1-x);
        else return min(query(root,0,g+r-1,0,g+r-1-x),query(root,0,g+r-1,(g-x+g+r)%(g+r),g+r-1));
    }
}tr1,tr2;
signed main()
{
    scanf("%lld%lld%lld",&n,&g,&r);
    for (int i=1;i<=n+1;i++) {
        int t; scanf("%lld",&t); s[i]=s[i-1]+t;
    }
    f[n]=s[n+1]-s[n]; tr1.insert(tr1.root,0,g+r-1,s[n]%(g+r),n);
    for (int i=n-1;i>=1;i--) {
        int j = tr1.query(g+r-s[i]%(g+r));
        if (j == inf) {
            f[i] = s[n+1]-s[i];
        } else {
            f[i] = f[j] + (s[j]-s[i]) + g+r-(s[j]-s[i])%(g+r);
        }
        tr1.insert(tr1.root,0,g+r-1,s[i]%(g+r),i);
    }
    for (int i=1;i<=n;i++) tr2.insert(tr2.root,0,g+r-1,s[i]%(g+r),i);
    int q; scanf("%lld",&q);
    while (q--) {
        int t; scanf("%lld",&t);
        int p = tr2.query(t%(g+r));
        if (p == inf) { printf("%lld\n",t+s[n+1]); continue;}
        else {
            printf("%lld\n",s[p]+t+g+r-(s[p]+t)%(g+r)+f[p]);
        }
    }
    return 0;
}
wedding.cpp

相关文章: