码迷,mamicode.com
首页 > 其他好文 > 详细

2021-ICPC昆明站M-Stone Games(思维+主席树)

时间:2021-04-08 12:53:57      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:div   icp   splay   最小   ber   pre   main   strong   line   

题目大意:

给你n个数字,m个询问,每次询问[l,r]

求[l,r]这个区间内的数不能够构成的最小正整数(下文称其为区间Mex(并不是严格的区间Mex))

题目思路:

我们逐步解决这个问题

Q1:

如何计算一个区间的MEX ?

假设当前区间[L,R]能够表示的数是[1,x]此时mex = x+1

然后我们扩展这个区间,也就是扩展到a[R+1]这个数字,区间Mex如何变化?

如果a[R+1]>x+1,那么无论如何都无法构成x+1这个数字,那么此时能够表示的数还是[1,x],区间Mex还是x+1

如果a[R+1]<=x+1,把原本能够构成的每个数字也就是[1,x]全部加这个数字a[R+1],所得集合取并集,得到新的能够表示的数的区间[1,x+a[R+1]],区间Mex变为x+a[R+1]+1;

重复上述操作,可以一直扩展

Q2----001:时间复杂度的考虑:

如果对于每次询问l,r,我们都暴力从l扩展到r,那么复杂度为O(q*n),显然这里需要一种更为优秀的算法

我们再仔细观察Q1中的扩展操作

如果这个区间内的数字是从小到大排好序的,那么每次扩展的时候,我们查询区间内的小于等于x+1的所有的值的和,记为sum,因为sum是可以扩展上的,所以扩展区间能够表示的数字为[1,x+sum],那么Mex也被扩大到了x+sum+1,那么我们再次查询区间内小于等于Mex的数字,这个时候,肯定多出来了一部分数字,记为up。

那么下次被扩扩展的肯定就是up这一部分数字,如果x+sum+up<Mex,那么此时Mex就是答案来,否则,继续重复上述操作扩展

而这样的扩展操作复杂度为O(log(max[a[i]]]))

Q2-002(另一种扩展的方式/理解)

 


这里的up表示区间能够表示的最大的数字

x为对于区间当前能够表示的区间的内的数字和

[1,up]   ---  x
能够扩展的情况:  x<=up+1   [1,up+x]
 
对于区间[L,R]
假设Mex=1
也就是区间[0,0] == [1,up]
待插入的数是[L,R]中小于等于up+1的数字   假设值为sum
 
如果sum<=up+1
[0,up] --> [0,up+sum]
 
但是一个区间内的数字为了避免重复计算
我们选择同时移动左端点
[0,up]-->[up+1,up+sum]

 

 

Q3--001:怎么实现Q2的扩展:


首先,对每个区间的排序也是不现实的,这里我们可以用一个桶记录每个数字出现的次数

因为我们每次询问的是一个区间,而我们查询的是一个区间内某个范围内的数字和(而且还有实现上述的排序),这个时候就需要用可持久化权值线段树

可以每次查询小于Mex的范围的所有数字sum

看看新扩展出来的那部分数字的和加上原来的和是否大于等于Mex,如果是继续扩展

否则输出答案

Q3-002:

查询操作变为查询区间[left+1,right+1]的和

 

 

CODE-001:

技术图片
ll  rt[maxn];
ll n, val[maxn], qq, indexx, TOP = 1000000001;
struct node
{
    ll  l, r;
    ll  s;
} a[maxn * 33];
void insert(ll  pre, ll  &now, ll l, ll r, ll num)
{
    now = ++indexx;
    a[now] = a[pre];
    a[now].s += num;
    if(l == r) return ;
    ll mid = (l + r) >> 1ll;
    if(num <= mid) insert(a[pre].l, a[now].l, l, mid, num);
    else           insert(a[pre].r, a[now].r, mid + 1, r, num);
}
ll query(ll v, ll  u, ll l, ll r, ll ql, ll qr)
{
    ll ans = 0;
    ll sum = a[u].s - a[v].s;
    if(sum == 0) return 0;
    if(ql <= l && qr >= r)  return sum;
    ll  mid = (l + r) >> 1ll;
    if(ql <= mid) ans += query(a[v].l, a[u].l, l, mid, ql, qr);
    if(qr > mid) ans  += query(a[v].r, a[u].r, mid + 1, r, ql, qr);
    return ans;
}
int main()
{
    n = read();
    qq = read();
    rep(i, 1, n) val[i] = read(), insert(rt[i - 1], rt[i], 1ll, TOP, val[i]);
    ll laast = 0ll;

    while(qq--)
    {
        ll x, y;
        x = read(), y = read();
        x += laast, y += laast;
        x %= n, x++, y %= n, y++;
        if(x > y) swap(x, y);
        ll  left = 1ll, right = 1ll;
        while(query(rt[x - 1], rt[y], 1ll, TOP, left, right) >= right) right   = query(rt[x - 1], rt[y], 1, TOP, left, right) + 1ll ;
        printf("%lld\n", right);
        laast = right;
    }
    return 0;
}
View Code

 

CODE-002:

技术图片
ll  rt[maxn];
int n, val[maxn], qq, indexx, TOP = 1000000001;
struct node {
    ll  l, r;
    ll  s;
} a[maxn * 33];
void insert(ll  pre, ll  &now, int l, int r, ll num) {
    now = ++indexx;
    a[now] = a[pre];
    a[now].s += num;
    if(l==r) return ;
    ll mid = (l + r) >> 1ll;
    if(num <= mid) insert(a[pre].l, a[now].l, l, mid, num);
    else           insert(a[pre].r, a[now].r, mid + 1, r, num);
}
ll query(ll v, ll  u, ll l, ll r, ll ql, ll qr) {
    ll ans = 0;
    ll sum = a[u].s - a[v].s;
    if(sum==0) return 0;
    if(ql <= l && qr >= r)  return sum;
    ll  mid = (l + r) >> 1ll;
    if(ql <= mid) ans += query(a[v].l, a[u].l, l, mid, ql, qr);
    if(qr > mid) ans  += query(a[v].r, a[u].r, mid + 1, r, ql, qr);
    return ans;
}
int main() {
    n = read();
    qq = read();
    rep(i, 1, n) val[i] = read(),insert(rt[i - 1], rt[i], 1ll, TOP, val[i]);
    ll laast=0ll;
 
    while(qq--) {
        ll x, y;
        x = read(), y = read();
        x+=laast,y+=laast;
        x%=n,x++,y%=n,y++;
        if(x>y) swap(x,y);
        ll  left = 0ll, right = 0ll;
        while(query(rt[x-1], rt[y], 1ll, TOP, left+1, right+1)) {
            ll zhi = query(rt[x-1], rt[y], 1ll, TOP, left+1, right+1);
            left = right+1ll;
            right= right+ zhi;
        }
        printf("%lld\n",right+1ll);
        laast = right+1ll;
    }
    return 0;
}
View Code

 

2021-ICPC昆明站M-Stone Games(思维+主席树)

标签:div   icp   splay   最小   ber   pre   main   strong   line   

原文地址:https://www.cnblogs.com/UpMing/p/14624021.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!