清北NOIP2017金秋冲刺训练营学业水平测试赛题解

本文解析了ACM竞赛中的三道题目,包括使用集合或伸展树解决T1,通过发现树的直径特性来解决T2,以及采用区间树状数组求解T3中连续未出现过的最小值的问题。

前言:竟然AK了。

题解:

T1:

随便什么set,或有毅力者手写Splay就能过。

T2:

可以YY出,那条路一定是树的直径。画个图可以发现,假如有更优的路径那一定长于树的直径,这是不存在的。那么求出来后bfs一次就好了。

T3:

求区间两个连续的未出现过的最小值。
可以分开两次做,第一次将1 2,3 4,5 6……合并在一起,求区间mex。
第二次将2 3,4 5,6 7……合并在一起,求区间mex。
然后比较下,就没了。
code:

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
using namespace std;
struct trnode{
    int lc,rc,c;
}tr[6500010];int tot=0,root[200010],md;
struct node{
    int l,r;
}q[200010];
int n,m,t;
int a[200010],ans[200010];
void update(int &x,int froot,int l,int r,int k,int c)
{
    x=++tot;
    tr[x]=tr[froot];
    if(k<l||k>r) return;
    if(l==r){tr[x].c=c;return;}
    int mid=(l+r)/2;
    if(k<=mid) update(tr[x].lc,tr[froot].lc,l,mid,k,c);
    else update(tr[x].rc,tr[froot].rc,mid+1,r,k,c);
    tr[x].c=min(tr[tr[x].lc].c,tr[tr[x].rc].c);
}
int findans(int x,int k,int l,int r)
{
    if(l==r) return l;
    int lc=tr[x].lc,lcc=tr[lc].c,mid=(l+r)/2;
    if(lcc<k) return findans(tr[x].lc,k,l,mid);
    else return findans(tr[x].rc,k,mid+1,r);
}
int getnum(int x,int l,int r,int k)
{
    if(!x) return 0;
    if(l==r) return tr[x].c;
    int mid=(l+r)/2;
    if(k<=mid) return getnum(tr[x].lc,l,mid,k);
    else return getnum(tr[x].rc,mid+1,r,k);
}
void cc(int &x,int y)
{
    if(x==-1) x=y;
    x=min(x,y);
}
int main()
{
    scanf("%d %d %d",&n,&m,&t);
    for(int i=1;i<=m;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=t;i++) scanf("%d %d",&q[i].l,&q[i].r);
    memset(ans,-1,sizeof(ans));
    md=n/2;
    for(int i=1;i<=m;i++) update(root[i],root[i-1],1,md,(a[i]+1)/2,i);
    for(int i=1;i<=t;i++)
    {
        int l=q[i].l,r=q[i].r;
        int tmp=findans(root[r],l,1,md);
        if(getnum(root[r],1,md,tmp)>=l) continue;
        ans[i]=(tmp-1)*2+1;
    }
    memset(tr,0,sizeof(tr));
    memset(root,0,sizeof(root));tot=0;
    md=(n-1)/2;
    for(int i=1;i<=m;i++) update(root[i],root[i-1],1,md,(a[i])/2,i);
    for(int i=1;i<=t;i++)
    {
        int l=q[i].l,r=q[i].r;
        int tmp=findans(root[r],l,1,md);
        if(getnum(root[r],1,md,tmp)>=l) continue;
        cc(ans[i],tmp*2);
    }
    for(int i=1;i<=t;i++)
    {
        if(ans[i]==-1) printf("-1 -1\n");
        else printf("%d %d\n",ans[i],ans[i]+1);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值