HGOI 20191030am 题解

Problem A 腿部挂件

  给出$n$个数的序列$a_i$,支持$T$次操作。

  每次操作形如$x , l , r$,计算$\max_{i = l}^{r} (a_i \oplus x)$的值。

  对于$100\%$的数据满足$1 \leq n \leq 2 \times 10^5 , 0 \leq a_i \leq 10^9$

Solution : 

  通常可以使用可持久化字典树求解,但是这里可以用字典树套vector来做。

  这样当当前节点vector有一个在$l,r$的数中就往下走,还是按照以前的贪心策略,从高位到低位贪心。

  这样做的时间复杂度是$O(n {log_2} ^2 n)$

#include<bits/stdc++.h>
#define pb push_back
using namespace std;
struct node
{
    vector<int>p;
    int ls,rs;
    node(){ls=rs=-1;}
}tr[6100000];
int n,m,a,x,y,cnt,o[50];
void push(int k,int p)
{
    for(int i=30-1,nw=1;i>=0;i--)
    {
        if(k&o[i])
        {
            if(tr[nw].ls==-1)tr[nw].ls=++cnt;
            nw=tr[nw].ls;
        }
        else
        {
            if(tr[nw].rs==-1)tr[nw].rs=++cnt;
            nw=tr[nw].rs;
        }
        tr[nw].p.pb(p);
    }
}
int query(int k,int l,int r)
{
    int ans=0;
    for(int i=30-1,nw=1;i>=0;i--)
    {
        if(k&o[i])
        {
            if(tr[nw].rs==-1||*(tr[tr[nw].rs].p.end()-1)<l||*lower_bound(tr[tr[nw].rs].p.begin(),tr[tr[nw].rs].p.end(),l)>r)
                nw=tr[nw].ls,ans+=o[i];
            else nw=tr[nw].rs;
        }
        else
        {
            if(tr[nw].ls==-1||*(tr[tr[nw].ls].p.end()-1)<l||*lower_bound(tr[tr[nw].ls].p.begin(),tr[tr[nw].ls].p.end(),l)>r)
                nw=tr[nw].rs;
            else nw=tr[nw].ls,ans+=o[i];
        }
    }
    return ans^k;
}
int main()
{
    freopen("hugclose.in","r",stdin);
    freopen("hugclose.out","w",stdout);
    scanf("%d%d",&n,&m),o[0]=cnt=1;
    for(int i=1;i<30;i++)o[i]=o[i-1]*2;
    for(int i=1;i<=n;i++)scanf("%d",&a),push(a,i);
    for(int i=1;i<=m;i++)scanf("%d%d%d",&a,&x,&y),printf("%d\n",query(a,x+1,y+1));
    return 0;
}
hugclose.cpp

Problem B 走夜路

  一条直线上有n+1个充电站,一开始你在第一个充电站,每个充电站都能以某个代价充电

  每走一个单位距离就要耗费一单位电,求按顺序走完所有充电站的最小代价

  对于$100\%$的数据满足$1 \leq n \leq 5\times 10^5$

Solution  :

   考虑一种贪心,依次考虑每一个充电站。

   下一个充电站优先级从高到低依次是:

  • 当前充电站以后第一个费用小于当前充电站(此时,直接在当前充电站充满恰好能到下个充电站的电,然后直接走到那个充电站即可)
  • 终点(如果当前充电站冲一定的点之后能到终点,那么就直接到达终点)
  • 在充满电后能走到的所有充电站中,费用最小的充电站(此时需要先在当前充电站充满电后再移动)

  可以考虑使用单调栈来求对于每个位置右侧第一个小于它的数,(从前往后加入数,满足栈单调增,弹栈的时候记录即可)

  然后再使用小常数的$st$表来求出静态区间最小值并且维护标号。

  所以,本题就是$O(n log_2 n)$的复杂度了。

#pragma GCC optimize(3)
# include <bits/stdc++.h>
using namespace std;
const int N=500000+10;
int st[1<<20][20],id[1<<20][20];
# define int long long 
int n,T,d[N],a[N],r[N];
stack<int>s;
int dist(int x,int y) { if (x>y) swap(x,y); return d[y-1]-d[x-1];}
namespace fast_IO{
    const int IN_LEN = 10000000, OUT_LEN = 10000000;
    char ibuf[IN_LEN], obuf[OUT_LEN], *ih = ibuf + IN_LEN, *oh = obuf, *lastin = ibuf + IN_LEN, *lastout = obuf + OUT_LEN - 1;
    inline char getchar_(){return (ih == lastin) && (lastin = (ih = ibuf) + fread(ibuf, 1, IN_LEN, stdin), ih == lastin) ? EOF : *ih++;}
    inline void putchar_(const char x){if(oh == lastout) fwrite(obuf, 1, oh - obuf, stdout), oh = obuf; *oh ++= x;}
    inline void flush(){fwrite(obuf, 1, oh - obuf, stdout);}
    int read(){
        int x = 0; int zf = 1; char ch = ' ';
        while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar_();
        if (ch == '-') zf = -1, ch = getchar_();
        while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar_(); return x * zf;
    }
    void write(int x){
        if (x < 0) putchar_('-'), x = -x;
        if (x > 9) write(x / 10);
        putchar_(x % 10 + '0');
    }
}
using namespace fast_IO;
void build() {
    memset(st,0x3f,sizeof(st));
    for (int i=1;i<=n;i++) st[i][0]=a[i],id[i][0]=i;
    for (int i=1;i<=19;i++)
        for (int j=1;j<=n;j++)
            if (st[j][i-1]<st[j+(1<<(i-1))][i-1]) {
                st[j][i] = st[j][i-1];
                id[j][i] = id[j][i-1];
            } else {
                st[j][i] = st[j+(1<<(i-1))][i-1];
                id[j][i] = id[j+(1<<(i-1))][i-1];
            }
}
int LOG[N];
int query(int l,int r) {
    int k=LOG[r-l+1];
    if (st[l][k] < st[r-(1<<k)+1][k]) return id[l][k]; 
    else return id[r-(1<<k)+1][k];
}
signed main()
{
    n=read();T=read();
    for (int i=1;i<=n;i++) LOG[i] = log2(i);
    for (int i=1;i<=n;i++) d[i]=read(),a[i]=read();
    build();
    while (!s.empty()) s.pop();
    for (int i=n;i>=1;i--) {
        while (!s.empty()&&a[s.top()]>=a[i]) s.pop();
        if (s.empty()) r[i]=n; else r[i]=s.top()-1;
        s.push(i);
    }
    for (int i=1;i<=n;i++) 
        r[i] = (r[i]==n)?(-1):(r[i]+1),
        d[i] += d[i-1];
    int now = 1, res = 0;
    int cost = 0;
    while (true) { 
        if (r[now]!=-1 && dist(r[now] , now) <= T) {
            if (res >= dist(r[now], now)) res-=dist(r[now],now);
            else  cost +=  1ll * a[now]*(dist(r[now], now)-res),res = 0;
            now = r[now];
        } else if (dist(n+1,now) <= T) {
            if (res >= dist(n+1, now)) res-=dist(n+1,now);
            else cost += 1ll * a[now]*(dist(n+1,now)-res),res = 0;
            break;
        } else {
            cost += 1ll * (T-res)*a[now];
            int l = now+1, r = n , ans = -1;
            while (l<=r) {
                int mid = (l+r) >> 1;
                if (dist(mid,now)<=T) ans=mid,l=mid+1;
                else r = mid-1;
            }
            if (ans == -1) {
                write(-1); flush(); return 0;
            }
            int to = query(now+1,ans);
            res = T - dist(to , now);
            now = to;
        }
    }
    write(cost);  flush(); 
    return 0;
}
wayhome.cpp

Problem C 宝石专家

 给出$n$个数的序列$a_i$,处理$T$个询问,

 每个询问形如$l,r$,请输出在$a_l ... a_r$中两个相同的数的最近距离。

 若$a_x = a_y$,那么距离定义为$|x- y|$

 对于$100\%$的数据,满足$1 \leq n,T\leq 2 \times 10^5$

Solution : 

  首先,我们发现对于相同的数,在一些位置,只有相邻的两个数所构成的线段有意义。

  所以需要处理的小线段长度在$n$这个级别。

  我们考虑将操作离线,放在一个$[1,n]$的数轴上,并考虑从$n$到$1$依次扫。

  如果当前遇到了一个小线段的左端点$l$,那么在线段树中$[r,n]$用这条线段的权值$r-l$来更新答案。

  所以,线段树的意义是扫到当前位置,询问线段的右端点的答案。

  所以,如果遇到一个询问线段,那么直接求出$[l,r]$的最小值即可,但是由于值的单调性,直接单点求出$r$的值即可。

  时间复杂度就是$O(n log_2 n)$。

#pragma GCC optimize(3)
# include <bits/stdc++.h>
# define inf (0x3f3f3f3f)
using namespace std;
const int N = 2e5 + 10;
vector<int>tmp;
vector<int>v[N];
vector< pair<int,int> >r[N],qes[N];
int n,m,a[N],ans[N];
namespace fast_IO{
    const int IN_LEN = 10000000, OUT_LEN = 10000000;
    char ibuf[IN_LEN], obuf[OUT_LEN], *ih = ibuf + IN_LEN, *oh = obuf, *lastin = ibuf + IN_LEN, *lastout = obuf + OUT_LEN - 1;
    inline char getchar_(){return (ih == lastin) && (lastin = (ih = ibuf) + fread(ibuf, 1, IN_LEN, stdin), ih == lastin) ? EOF : *ih++;}
    inline void putchar_(const char x){if(oh == lastout) fwrite(obuf, 1, oh - obuf, stdout), oh = obuf; *oh ++= x;}
    inline void flush(){fwrite(obuf, 1, oh - obuf, stdout);}
    int read(){
        int x = 0; int zf = 1; char ch = ' ';
        while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar_();
        if (ch == '-') zf = -1, ch = getchar_();
        while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar_(); return x * zf;
    }
    void write(int x){
        if (x < 0) putchar_('-'), x = -x;
        if (x > 9) write(x / 10);
        putchar_(x % 10 + '0');
    }
}
using namespace fast_IO;
struct Seg {
    int val,tag;
    Seg() { val = tag = inf;}
}tr[N<<2];
# define lson ls,l,mid
# define rson rs,mid+1,r
# define mid (l+r>>1)
# define ls (x<<1)
# define rs (x<<1|1)
void down(int x) {
    tr[ls].val=min(tr[ls].val,tr[x].tag);
    tr[rs].val=min(tr[rs].val,tr[x].tag);
    tr[ls].tag=min(tr[ls].tag,tr[x].tag);
    tr[rs].tag=min(tr[rs].tag,tr[x].tag);
    tr[x].tag = inf;
}
void update(int x,int l,int r,int opl,int opr,int val) {
    if (opl <= l && r <= opr) {
        tr[x].tag = min(tr[x].tag , val);
        tr[x].val = min(tr[x].val , val);
        return;
    }
    down(x);
    if (opl<=mid) update(lson,opl,opr,val);
    if (opr> mid) update(rson,opl,opr,val);
    tr[x].val = min(tr[ls].val , tr[rs].val);
}
int query(int x,int l,int r,int pos) {
    if (l == r) return tr[x].val;
    down(x);
    if (pos<=mid) return query(lson,pos);
    else return query(rson,pos);
}
int main()
{
    n=read();m=read(); 
    for (int i=1;i<=n;i++) {
        tmp.push_back(a[i]=read());
    }
    sort(tmp.begin(),tmp.end());
    tmp.erase(unique(tmp.begin(),tmp.end()),tmp.end());
    for (int i=1;i<=n;i++) {
        a[i] = lower_bound(tmp.begin(),tmp.end(),a[i]) - tmp.begin()+1;
        v[a[i]].push_back(i);
    }
    for (int i=1;i<=n;i++) {
        if (v[i].size() < 2) continue;
        for (int j=0;j<v[i].size()-1;j++)
            r[v[i][j]].push_back(make_pair(v[i][j+1],v[i][j+1]-v[i][j])); 
    }
    for (int i=1;i<=m;i++) {
        int l=read(),r=read(); 
        qes[l].push_back(make_pair(r,i));
    }
    for (int i=n;i>=1;i--) {
        int sz = r[i].size();
        if (sz) {
            for (int j=0;j<sz;j++) 
                update(1,1,n,r[i][j].first,n,r[i][j].second);
        }
        sz = qes[i].size();
        if (sz) {
            for (int j=0;j<sz;j++)
             ans[qes[i][j].second] = query(1,1,n,qes[i][j].first);
        }
    }
    for (int i=1;i<=m;i++) {
        write((ans[i]>=inf)?-1:ans[i]); putchar_('\n');
    }
    flush();
    return 0;
}
jewel.cpp

 

posted @ 2019-10-30 14:35  ljc20020730  阅读(168)  评论(0编辑  收藏  举报