破防了,什么 SCOI2024 Day1 翻版,一道题可能拿 100pts,一道题大多数人拿 10pts,还有一道不可做,队线 110/lh
T1
去他妈的煞笔构史题解,说了跟说了一样。
这个是真的不会。容易想到对二进制每一位开一颗权值线段树或者别的啥维护,然后我就不会了……
考虑将每颗权值线段树对应处理的区间设为 \([0,2^{i+1}-1]\),每次加入元素或删除元素时将其 \(\text{mod}\:\: 2^{i+1}\) 然后放到对应的权线上维护,开个 map 维护每个数的出现次数即可。
然后处理全局加操作,考虑维护一个全局的加法标记,加了之后相当于将每颗权值线段树的节点向后平移,但是我们已经设定了每颗权线的处理范围,每次平移时可能会超出处理范围,这个时候就在查询时分开查。对于要在权线上查询的区间 \([l,r]\),若 \(l\le r\) 则直接查询 \([l,r]\),否则分开查 \([l,lim]\) 和 \([0,r]\),\(lim\) 是每颗权线的处理上界。查询中 \(l=\text{get}(2^x,x),r=\text{get}(2^{x+1}-1,x)\)
还有既然我们将全局加的值看作是一个向右的平移量,那么每次查询以及加入或删除元素时都要减去这个平移量才能在原来的权值线段树区间上维护。
没了。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define in inline
#define ll long long
#define ls now<<1
#define rs now<<1|1
const ll N=1145140,M=1919810;
ll n,tag;
struct xx{
ll sum[N],v;
in void update(ll now,ll l,ll r,ll tar,ll k){
sum[now]+=k;
if(l==r) return;
ll mid=(l+r)>>1;
if(tar<=mid) update(ls,l,mid,tar,k);
else update(rs,mid+1,r,tar,k);
}
in ll query(ll now,ll l,ll r,ll x,ll y){
if(l>=x&&r<=y) return sum[now];
ll mid=(l+r)>>1,ans=0;
if(x<=mid) ans+=query(ls,l,mid,x,y);
if(y>mid) ans+=query(rs,mid+1,r,x,y);
return ans;
}
in ll que(ll l,ll r){
if(l<=r) return query(1,0,v,l,r);
return query(1,0,v,l,v)+query(1,0,v,0,r);
}
}t[20];
map <ll,ll> ma;
ll get(ll x,ll mod){
mod=1<<(mod+1);
return (x%mod+mod)%mod;
}
int main(){
//freopen("data.in","r",stdin);
//freopen("Heshu.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin>>n;
for(int i=0;i<18;++i) t[i].v=(1<<(i+1))-1;
for(int i=1;i<=n;++i){
ll opt,x,w;
cin>>opt>>x;
if(opt==0){
x-=tag,++ma[x];
for(int j=0;j<18;++j) t[j].update(1,0,t[j].v,get(x,j),1);
}
if(opt==1){
x-=tag,w=ma[x],ma[x]=0;
for(int j=0;j<18;++j) t[j].update(1,0,t[j].v,get(x,j),-w);
}
if(opt==2) tag+=x;
if(opt==3){
ll l=get((1<<x)-tag,x),r=get((1<<(x+1))-1-tag,x);
cout<<t[x].que(l,r)<<'\n';
}
}
return 0;
}
T2
数论,会你妈
T3
有标号无向连通图计数,会你妈
标签:省选,ll,查询,权线,权值,每颗,线段,模拟,20240726 From: https://www.cnblogs.com/heshuwan/p/18325640