CF896C Willem, Chtholly and Seniorious

https://www.luogu.com.cn/problem/CF896C

珂朵莉树\((ODT)\)

珂朵莉树需要使用\(set\)\(C++ STL\)

首先熟悉一下\(set\)的部分操作:

\(1.\)把结构体丢进\(set\),需要重载运算符

\(2.\)\(lower\_bound()\)函数,返回第一个大于等于查询数的位置的迭代器

\(3.\)\(insert()\)函数有返回值,是一个\(pair\)类型,其\(first\)值代表一个迭代器,即插入值的位置,其\(second\)值代表一个\(bool\)类型,代表是否插入成功(若\(set\)中原本有重复的元素,则插入失败)

\(4.\)\(erase(l,r)\)(\(l,r\)为迭代器)可以删除\([l,r)\)的所有元素(注意左闭右开)

\(5.\)\(erase()\)循环删除时,必须\(erase(i++)\),而不能在循环中\(i++\),同时\(erase(i)\)(把迭代器都删了,怎么\(i++\)

错误代码:

for (IT i=L;i!=R;i++)
        s.erase(i);

正确代码:

for (IT i=L;i!=R;)
        s.erase(i++);

\(6.\)改变\(set\)中结构体元素,必须定义\(mutable\)(表示元素永久可以改变,否则默认\(const\),即恒定,例:\(mutable\quad long \quad long\)

\(7.\)指针\(it->l\),相当于\((*it).l\)

基础操作见此处

然后讲珂朵莉树的构造,一个结构体代表一段数值相同的区间

如果一段区间一部分值改变,就直接拆成两个区间

如果一些区间被该成相同的值,直接删光,重新建一个区间

区间互不重叠,所有区间的并集为总区间

操作全部大暴力遍历,求值

\(Code:\)

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<set>
#include<vector>
#define ll long long
using namespace std;
int n,m,op,l,r;
ll seed,vmax,x,y;
struct ODT
{
    int l,r;
    mutable ll v;
    ODT (int x=0,int y=0,ll z=0)
    {
        l=x,r=y,v=z;
    }
    bool operator < (const ODT w) const
    {
        return l<w.l;
    }
};
ll ksm(ll x,ll y,ll p)
{
    x%=p;
    ll ans=1;
    while (y)
    {
        if (y&1)
            ans=ans*x%p;
        x=x*x%p;
        y >>=1;
    }
    return ans;
}
set<ODT>s;
#define IT set<ODT>::iterator
IT split(int pos)//找到pos所在的区间[l,r],若l<pos≤r,拆分成[l,pos]),[pos,r]
{
    IT it=s.lower_bound(ODT(pos,pos,0));
    if (it!=s.end() && it->l==pos)
        return it;
    it--;
    int L=it->l,R=it->r;
    ll V=it->v;
    s.erase(it);
    s.insert(ODT(L,pos-1,V));
    return s.insert(ODT(pos,R,V)).first;
}
void add(int l,int r,ll V)//区间加
{
    IT L=split(l);
    IT R=split(r+1);
    for (IT i=L;i!=R;i++)
        i->v+=V;
}
void fz(int l,int r,ll V)//区间赋值
{
    IT L=split(l),R=split(r+1);
    s.erase(L,R);
    s.insert(ODT(l,r,V));
}
bool cmp(IT x,IT y)
{
    return x->v<y->v;
}
#define VIT vector<IT>::iterator
ll rk(int l,int r,int x)//求区间指定排名的数
{
    vector<IT>g;
    g.clear();
    IT L=split(l),R=split(r+1);
    for (IT i=L;i!=R;i++)
        g.push_back(i);
    sort(g.begin(),g.end(),cmp);
    int o=0;
    for (VIT i=g.begin();i!=g.end();i++)
    {
        o+=(*i)->r-(*i)->l+1;
        if (o>=x)
            return (*i)->v;
    }
}
ll gets(int l,int r,ll x,ll y)//求区间幂和
{
    ll ans=0;
    IT L=split(l),R=split(r+1);
    for (IT i=L;i!=R;i++)
        ans=(ans+ksm(i->v,x,y)*(ll)(i->r-i->l+1))%y;
    return ans;
}
ll rnd()
{
    ll ret=seed;
    seed=(seed*7+13)%1000000007;
    return ret;
}
int main()
{
    scanf("%d%d%lld%lld",&n,&m,&seed,&vmax);
    for (int i=1;i<=n;i++)
        s.insert(ODT(i,i,(rnd()%vmax)+1));
    s.insert(ODT(n+1,n+1,0));
    for (int i=1;i<=m;i++)
    {
        x=y=0;
        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;
        switch (op)
        {
            case 1:
                add(l,r,x);
                break;
            case 2:
                fz(l,r,x);
                break;
            case 3:
                printf("%lld\n",rk(l,r,x));
                break;
            case 4:
                printf("%lld\n",gets(l,r,x,y));
                break;
        }
    }
}
posted @ 2020-07-28 18:50  GK0328  阅读(211)  评论(0编辑  收藏  举报