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;
}
}
}