codeforces 915 E 896 C 珂朵莉树

珂朵莉树(Chtholly Tree),一种基于std::set的暴力数据结构,是由某毒瘤在2017年的一场CF比赛中提出的数据结构,原名老司机树(Old Driver Tree,ODT)。由于第一个以珂朵莉树为正解的题目的背景以《末日时在做什么?有没有空?可以来拯救吗?》中的角色——珂朵莉为主角,所以这个数据结构又被称为珂朵莉树(Chtholly Tree)她主要处理区间修改、查询问题,在数据随机的前提下有着优秀的复杂度。

加了 assign 的珂朵莉树在随机数据下跑得飞快,节点数达到了 log 级别。也就是说,珂朵莉树的高效是由随机分配的Assign保证的。

如果一个题目没有区间赋值操作或者有数据点没有赋值操作,或者数据很不随机,请不要把珂朵莉树当正解打。

珂朵莉树应用的范围很窄......

1.、Q:为什么要Split(r + 1)
       A:便于取出 [l,r+1)[l,r+1) 的部分,即 [l,r][l,r]

2、Q:为什么要Split(r + 1),Split(l)
      A:因为如果先Split(l),返回的迭代器会位于所对应的区间以ll为左端点,此时如果rr也在这个节点内,就会导致Split(l)返回的迭代器被erase掉,导致RE。

3、Q:这些操作里面全是Split,复杂度理论上会退化成暴力(不断分裂直到无法再分),怎么让它不退化?

      A:assign操作。

915 E

题意:有n个元素,这n个元素最初的状态都为1,对n个元素做q次操作,每次操作把给定区间置0或置1,每次操作之后都要输出这n个元素中1的个数。

乍一看以为是线段树裸题,但是看到n的数据范围我放弃了......

网上看了博客,有三种方法来做这个题:1、珂朵莉树(ODT);2、set;3、线段树动态开点。

我看了用珂朵莉树的做法,听说珂朵莉树解决区间推平问题比较好用,然后就大概学了一下。

其实什么也没学懂,珂朵莉树的关键好像是split操作,然后其他的就比较暴力了。并且珂朵莉树的复杂度比较玄学,有些题目如果数据水的话能过,如果出题人专门弄数据卡你就玩完了,所以珂朵莉树不能当作正解......

推荐一个讲珂朵莉树的视频,里面也有这道题的大致讲解,好像是成电的大佬讲的:https://www.bilibili.com/video/av21651173from=search&seid=13578438704386102893

代码参考了下面这篇博客:https://www.cnblogs.com/bztMinamoto/p/9810833.html

下面这个代码,node 中 l , r , v 的含义表示区间 [ l ,  r ] 的和是 v 。

#include<bits/stdc++.h>
#define IT set<node>::iterator
using namespace std;
struct node
{
    int l,r;
    mutable int v;
    node(int L,int R=-1,int V=0):l(L),r(R),v(V) {}
    inline bool operator <(const node &b)const
    {
        return l<b.l;
    }
};
set<node> s;
int sum;

//把pos所在的区间[l,r]分离成[l,pos-1]和[pos+1,r]
IT split(int pos)
{
    IT it=s.lower_bound(node(pos));
    if(it!=s.end()&&it->l==pos) return it;
    --it;
    int l=it->l,r=it->r,v=it->v;
    s.erase(it);
    s.insert(node(l,pos-1,v?pos-l:0));
    return s.insert(node(pos,r,v?r-pos+1:0)).first;
}

//区间推平,把区间[l,r]的值置成v
void assign(int l,int r,int v)
{
    IT itr=split(r+1),itl=split(l);
    for(IT it=itl; it!=itr; ++it) sum-=it->v;
    s.erase(itl,itr),s.insert(node(l,r,v*(r-l+1)));
    sum+=v*(r-l+1);
}
int main()
{
    int n,q;
    scanf("%d%d",&n,&q);
    s.insert(node(1,n,n));
    sum=n;
    int l,r,p;
    while(q--)
    {
        scanf("%d%d%d",&l,&r,&p);
        assign(l,r,p-1);
        printf("%d\n",sum);
    }
    return 0;
}

896 C

题意:请你写一种奇怪的数据结构,支持:

  • 11 ll rr xx :将[l,r][l,r] 区间所有数加上xx
  • 22 ll rr xx :将[l,r][l,r] 区间所有数改成xx
  • 33 ll rr xx :输出将[l,r][l,r] 区间从小到大排序后的第xx 个数是的多少(即区间第xx 小,数字大小相同算多次,保证 1\leq1≤ xx \leq≤ r-l+1r−l+1 )
  • 44 ll rr xx yy :输出[l,r][l,r] 区间每个数字的xx 次方的和模yy 的值(即(\sum^r_{i=l}a_i^x∑i=lr​aix​ ) \mod ymody )

【输入格式】 这道题目的输入格式比较特殊,需要选手通过seedseed 自己生成输入数据。 输入一行四个整数n,m,seed,v_{max}n,m,seed,vmax​ (1\leq1≤ n,m\leq 10^{5}n,m≤105 ,0\leq seed \leq 10^{9}+70≤seed≤109+7 ,1\leq vmax \leq 10^{9},1≤vmax≤109 ) 其中n 表示数列长度,mm表示操作次数,后面两个用于生成输入数据。 题目中给出了生成数据的伪代码。

思路:珂朵莉树。

(这个题不知道为什么,用#define ll long long就报错,用typedef long long ll就行了,以前也遇到过这种情况,很奇怪)

下面这个代码,node 中 l , r , v 的含义表示区间 [ l ,  r ] 中每个数的值都是 v 。

#include<bits/stdc++.h>
#define Mod 1000000007
#define IT set<node>::iterator
typedef long long ll;
//#define ll long long
using namespace std;
ll a[100010];
struct node{
    ll l,r;
    mutable ll v;
    node(ll L,ll R=-1,ll V=0):l(L),r(R),v(V){}
    bool operator<(const node& p) const
    {
        return l<p.l;
    }
};
set<node> s;
ll seed;
ll rnd()
{
    ll ret=seed;
    seed=(seed*7+13)%Mod;
    return ret;
}
IT split(ll pos)
{
    IT it=s.lower_bound(node(pos));
    if(it!=s.end()&&it->l==pos)
        return it;
    --it;
    ll l=it->l,r=it->r,v=it->v;
    s.erase(it);
    s.insert(node(l,pos-1,v));
    return s.insert(node(pos,r,v)).first;
}
void assign(ll l,ll r,ll val)
{
    IT itr=split(r+1),itl=split(l);
    s.erase(itl,itr);
    s.insert(node(l,r,val));
}
void add(ll l,ll r,ll val)
{
    IT itr=split(r+1),itl=split(l);
    for(IT it=itl;it!=itr;it++)
        it->v+=val;
}
ll pow_mod(ll a,ll b,ll mod)
{
    ll ret=1;
    while(b)
    {
        if(b&1)
            ret*=a;
        a=a*a;
        b>>=1;
    }
    return ret;
}
ll pown(ll a, ll b, ll mod)
{
    ll res = 1;
    ll ans = a % mod;
    while (b)
    {
        if (b&1)
            res = res * ans % mod;
        ans = ans * ans % mod;
        b>>=1;
    }
    return res;
}
ll sum(ll l, ll r, ll ex, ll mod)
{
    IT itr = split(r+1),itl = split(l);
    ll res = 0;
    for (; itl != itr; ++itl)
        res = (res + (ll)(itl->r - itl->l + 1) * pown(ll(itl->v), ll(ex), ll(mod))) % mod;
    return res;
}
ll ask(ll l,ll r,ll k)
{
    vector<pair<ll,ll> > vp;
    IT itr=split(r+1),itl=split(l);
    vp.clear();
    for(;itl!=itr;itl++)
        vp.push_back(pair <ll,ll> (itl->v,itl->r-itl->l+1));
    sort(vp.begin(),vp.end());
    for(vector<pair<ll,ll> >::iterator it=vp.begin();it!=vp.end();it++)
    {
        k-=it->second;
        if(k<=0)
            return it->first;
    }
}
int main()
{
    ll n,m;
    ll vmax;
    scanf("%I64d%I64d%I64d%I64d",&n,&m,&seed,&vmax);
    ll op,l,r,x,y;
    for (ll i=1; i<=n; ++i)
    {
        a[i] = (rnd() % vmax) + 1;
        s.insert(node(i,i,a[i]));
    }
    for(ll i=1; i<=m; i++)
    {
        op=(rnd()%4)+1;
        l=(rnd()%n)+1;
        r=(rnd()%n)+1;
        if(l>r)
            swap(l,r);
        if (op == 3)
            x = (rnd()%(r - l + 1)) + 1;
        else
            x = (rnd()%vmax) + 1;
        if (op == 4)
            y = (rnd()%vmax) + 1;
        if(op==1)
            add(l,r,x);
        if(op==2)
            assign(l,r,x);
        if(op==3)
            printf("%I64d\n",ask(l,r,x));
        if(op==4)
            printf("%I64d\n",sum(l,r,x,y));
    }
    return 0;
}

另外,推荐两篇关于珂朵莉树写得比较好的博客:

https://www.cnblogs.com/WAMonster/p/10181214.html

https://www.cnblogs.com/yzhang-rp-inf/p/9443659.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值