Willem, Chtholly and Seniorious题解

Willem, Chtholly and Seniorious题解

题目传送:题目

题面翻译

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

  • 1 l r x :将 [l,r] 区间所有数加上 x
  • 2 l r x :将 [l,r] 区间所有数改成 x
  • 3 l r x :输出将 [l,r] 区间从小到大排序后的第 x 个数是的多少(即区间第$x$ 小,数字大小相同算多次,保证 1\leq x \leq r-l+1 )
  • 4 l r x y :输出 [l,r] 区间每个数字的 x 次方的和模 y 的值(即(\sum^r_{i=l}a_i^x ) \mod y )

【输入格式】
这道题目的输入格式比较特殊,需要选手通过$seed$ 自己生成输入数据。
输入一行四个整数 n,m,seed,v_{max} 1\leq n,m\leq 10^{5} ,0\leq seed \leq 10^{9}+7 ,1\leq vmax \leq 10^{9}
其中 n 表示数列长度, m 表示操作次数,后面两个用于生成输入数据。

思路分析

​ 这题是珂朵莉树的起源,珂朵莉树详见

1.区间加

​ 将每个节点都加一遍就行了。

void add(int l,int r,ll v){
    IT itr=split(r+1),itl=split(l);
    for(;itl!=itr;itl++){
        itl->v+=v;
    }
}

2.区间第k小

​ 也是非常暴力的思想,将每个节点存在 vector 里再 sort 一下。

ll Rank(int l,int r,int k){
    IT itr=split(r+1),itl=split(l);
    vector<PL> vt;
    for(;itl!=itr;itl++){ 
		int L=itl->l,R=itl->r;
	    ll V=itl->v;
     	vt.push_back(PL(V,R-L+1));
    }
    sort(vt.begin(),vt.end());
    for(VIT it=vt.begin();it!=vt.end();++it){
    	k-=it->second;
    	if(k<=0){
    		return it->first;
		}
	}
}

3.区间x次方和

​ 每个节点x次方加起来就行了,不要忘记乘以区间长度,还有就是对每个节点的值先模一个,不然会爆 $ long long $(还是非常暴力

ll sum(int l,int r,int x,int mod)
{
    IT itr = split(r+1),itl=split(l);
 	ll res=0;
    for (;itl!=itr;itl++){
		int L=itl->l,R=itl->r;
    	ll V=itl->v;
        V=V%mod;
        res =(res+(ll)(R-L+1)*bp(V,ll(x),ll(mod)))%mod;
    }
    return res;
}

​ 至此这道题就完成了,当然还有线段树的做法,实现比珂朵莉树难很多。

代码实现

#include<bits/stdc++.h>
#define IT set<node>::iterator
#define PL pair<long long,int>//first记录区间值,second记录区间长度 
#define VIT vector<PL>::iterator
using namespace std;
typedef long long ll;
const int N=1145141;
const int sM=1e9+7;
struct  node{
    int l,r;
    mutable ll v;
    node(int L,int R=0,ll V=0):l(L),r(R),v(V){}
    bool operator <(const node& a)const{return l<a.l;}
};
inline ll bp(ll a,ll b,ll M) {
  if (b==0) return 1;
  ll res = bp(a,b/2,M);
  if (b % 2)
    return(res*res)%M*a%M;
  else
    return (res*res)%M; 
}
set<node> chtolly;
IT split(int pos){ //分裂
    IT it=chtolly.lower_bound(node(pos));
    if(it !=chtolly.end()&&it->l==pos){return it;}
    it--;
    int l=it->l,r=it->r;
    ll v=it->v;
    chtolly.erase(it);
    chtolly.insert(node(l,pos-1,v));
    return chtolly.insert(node(pos,r,v)).first;
}
void assign(int l,int r,int v){//推平
    IT itr=split(r+1),itl=split(l);
    chtolly.erase(itl,itr);
    chtolly.insert(node(l,r,v));
}
void add(int l,int r,ll v){//区间加
    IT itr=split(r+1),itl=split(l);
    for(;itl!=itr;itl++){
        itl->v+=v;
    }
}
ll Rank(int l,int r,int k){//第k小
    IT itr=split(r+1),itl=split(l);
    vector<PL> vt;
    for(;itl!=itr;itl++){ 
		int L=itl->l,R=itl->r;
	    ll V=itl->v;
     	vt.push_back(PL(V,R-L+1));
    }
    sort(vt.begin(),vt.end());
    for(VIT it=vt.begin();it!=vt.end();++it){
    	k-=it->second;
    	if(k<=0){
    		return it->first;
		}
	}
}
ll sum(int l,int r,int x,int mod)//求和
{
    IT itr = split(r+1),itl=split(l);
 	ll res=0;
    for (;itl!=itr;itl++){
		int L=itl->l,R=itl->r;
    	ll V=itl->v;
        V=V%mod;
        res =(res+(ll)(R-L+1)*bp(V,ll(x),ll(mod)))%mod;
    }
    return res;
}
int n,m;
ll seed,vmax,a[N];
ll rnd(){
	ll ret=seed;
	seed=(seed*7+13)%sM;
	return ret;
}
int main(){
    cin>>n>>m>>seed>>vmax;
    for(int i=1;i<=n;i++){
    	a[i]=(rnd()%vmax)+1;
        chtolly.insert(node(i,i,a[i]));
    }
    chtolly.insert(node(n+1,n+1,0));
    while(m--){
        int l,r,x,y,op;
        op=int(rnd()%4)+1;
        l=int(rnd()%n)+1;
        r=int(rnd()%n)+1;
        if(l>r) {
			swap(l,r);
    	}
		if(op==3){
        	x=int(rnd()%(r-l+1))+1;
		}
		else{
			x=int(rnd()%vmax)+1;
		} 
		
        if(op==4){
        	y=int(rnd()%vmax)+1;
		}
		if(op==1){
			add(l,r,ll(x));
		}
		else if(op==2){
			assign(l,r,ll(x));
		} 
		else if(op==3){
			cout<<Rank(l,r,x)<<endl;
		}
		else {
			cout<<sum(l,r,x,y)<<endl;
		}
    }
    return 0;
}

​喜欢的记得点个赞。

1 个赞

好像啥也没讲啊。。。

我永远喜欢珂朵莉!

1 个赞

珂朵莉树讲解我已经发过了,这题板子题也没啥能讲的(