[做题记录]平衡树
记录一下平衡树的一些题目。
遇到了新的会更新。
(Ps:我不知道有些题目能不能算成平衡树的题目,因为在省选计划的[平衡树]模块里,又不想单独丢一个博客,就丢这里来好了,建议当做是数据结构做题记录的一部分来观看)。
[模板]平衡树
两版代码。
一版替罪羊。
一版无旋 \(treap\)。
替罪羊还是好写。
替罪羊
// code by fhq_treap
#include<bits/stdc++.h>
#define ll long long
#define N 300005
#define alpha 0.7
inline ll read(){
char C=getchar();
ll A=0 , F=1;
while(('0' > C || C > '9') && (C != '-')) C=getchar();
if(C == '-') F=-1 , C=getchar();
while('0' <= C && C <= '9') A=(A << 1)+(A << 3)+(C - 48) , C=getchar();
return A*F;
}
template <typename T>
void write(T x)
{
if(x < 0) {
putchar('-');
x = -x;
}
if(x > 9)
write(x/10);
putchar(x % 10 + '0');
return;
}
int tot;
struct P{
int l,r,v,sz,valid;
bool del;
inline void Newnode(int x){l = r = 0;sz = valid = 1;del = 0;v = x;}
}t[N << 2];
#define ls(o) t[o].l
#define rs(o) t[o].r
inline bool bad(int o){
return std::max(1.0 * t[ls(o)].sz,1.0 * t[rs(o)].sz) > alpha * t[o].sz;
}
inline void up(int u){
t[u].sz = t[ls(u)].sz + t[rs(u)].sz + !t[u].del;
t[u].valid = t[ls(u)].valid + t[rs(u)].valid + !t[u].del;
}
inline void dfs(int u,std::vector<int> & v){
if(!u)return ;
dfs(ls(u),v);
if(!t[u].del)v.push_back(u);
dfs(rs(u),v);
}
inline int build(std::vector<int> &v,int l,int r){
if(l >= r)return 0;
#define mid ((l + r) >> 1)
int u = v[mid];
ls(u) = build(v,l,mid);
rs(u) = build(v,mid + 1,r);
up(u);
return u;
}
inline void rebuild(int &u){
std::vector<int>v;
dfs(u,v);
u = build(v,0,(int)v.size());
}
inline void insert(int x,int &u){
if(!u){
u = ++ tot;
t[u].Newnode(x);
return ;
}
t[u].sz ++;t[u].valid ++;
if(x >= t[u].v)insert(x,rs(u));
else
insert(x,ls(u));
if(bad(u))
rebuild(u);
return ;
}
inline int getrank(int u,int x){
int ans = 1;
while(u){
if(t[u].v >= x)u = ls(u);
else{
ans += t[ls(u)].valid + !t[u].del;
u = rs(u);
}
}
return ans;
}
inline int findkth(int u,int x){
while(u){
if(!t[u].del && t[ls(u)].valid + 1 == x)
return t[u].v;
if(t[ls(u)].valid >= x)
u = ls(u);
else{
x -= t[ls(u)].valid + !t[u].del;
u = rs(u);
}
}
}
inline void Del(int u,int rk){
if(!t[u].del && rk == t[ls(u)].valid + 1){
t[u].del = 1;
-- t[u].valid;
return;
}
-- t[u].valid;
if(rk <= t[ls(u)].valid + !t[u].del)
Del(ls(u),rk);
else
Del(rs(u),rk - t[ls(u)].valid - !t[u].del);
// up(u);
}
int n,rt;
int main(){
scanf("%d",&n);
rt = 0;
while(n -- ){
int opt,x;
scanf("%d%d",&opt,&x);
if(opt == 1)insert(x,rt);
if(opt == 2)Del(rt,getrank(rt,x));
if(opt == 3)std::cout<<getrank(rt,x)<<std::endl;
if(opt == 4)std::cout<<findkth(rt,x)<<std::endl;
if(opt == 5)std::cout<<findkth(rt,getrank(rt,x) - 1)<<std::endl;
if(opt == 6)std::cout<<findkth(rt,getrank(rt,x + 1))<<std::endl;
}
}
无旋
//rest : 126 days
#include<bits/stdc++.h>
#define ll long long
#define N 100005
int ch[N][2];
ll va[N],key[N];
int siz[N];
inline ll randdom(){return rand() << 15 | rand();}
int cnt;
#define ls(x) ch[x][0]
#define rs(x) ch[x][1]
#define v(x) va[x]
#define c(x) key[x]
#define s(x) siz[x]
inline int New(ll s){++cnt;s(cnt) = 1,v(cnt) = s,c(cnt) = randdom();return cnt;}
inline void up(int u){s(u) = 1 + s(ls(u)) + s(rs(u));}
inline void split(int u,ll k,int &x,int &y){//x,y分裂的两颗树,x小于key,y大于key
if(!u){x = y = 0;return;}
if(v(u) <= k){x = u,split(rs(u),k,rs(u),y);}
else{y = u,split(ls(u),k,x,ls(u));}
up(u);
}
inline int merge(int x,int y){//max \in x < min \in y small root
if(!x || !y)return x + y;
if(c(x) < c(y))
{rs(x) = merge(rs(x),y);up(x);return x;}
else
{ls(y) = merge(x,ls(y));up(y);return y;}
}
int root,x,y,z;
inline void insert(ll a){
split(root,a,x,y);
root = merge(merge(x,New(a)),y);
}
inline void del(ll a){
split(root,a,x,z);
split(x,a - 1,x,y);
y = merge(ls(y),rs(y));
root = merge(x,merge(y,z));
}
inline int find(ll a){
split(root,a - 1,x,y);
int ans = s(x) + 1;
root = merge(x,y);
return ans;
}
inline ll kth(int u,ll k){
if(s(ls(u)) >= k)return kth(ls(u),k);
if(s(ls(u)) + 1 == k)return u;
return kth(rs(u),k - s(ls(u)) - 1);
}
inline ll pre(ll a){
split(root,a - 1,x,y);
ll ans = v(kth(x,s(x)));
root = merge(x,y);
return ans;
}
inline ll nex(ll a){
split(root,a,x,y);
ll ans = v(kth(y,1));
root = merge(x,y);
return ans;
}
int n;
int main(){
scanf("%d",&n);
for(int i = 1;i <= n;++i){
int opt;
ll x;
scanf("%d%lld",&opt,&x);
if(opt == 1)insert(x);
if(opt == 2)del(x);
if(opt == 3)std::cout<<find(x)<<std::endl;
if(opt == 4)std::cout<<v(kth(root,x))<<std::endl;
if(opt == 5)std::cout<<pre(x)<<std::endl;
if(opt == 6)std::cout<<nex(x)<<std::endl;
}
}
[NOI2005] 维护数列
直接就按照平衡树维护序列的操作就好。
这题区间赋值,区间最大字段和要维护七八个量,着实太难写。
拉了个能看的sol。
[NOI2005] 维护数列
#include<bits/stdc++.h>
using namespace std;
int stk[500005],len;
int inline read(){
int num=0;bool neg=0;char c=getchar();
while(c<'0'||c>'9'){if(c=='-') neg=1;c=getchar();}
while(c>='0'&&c<='9') num=num*10+c-'0',c=getchar();
return neg?-num:num;
}
struct fhqtreap{
int l,r,s,val,cov;bool lzy,cov_flg;
int sum,maxqz,maxhz,maxzd;
}T[500005];int idx,rt;
int NewNode(int v){
idx=stk[len--];T[idx].l=T[idx].r=T[idx].lzy=T[idx].cov=0;
T[idx].s=1;
T[idx].val=T[idx].sum=v;
T[idx].maxqz=T[idx].maxhz=max(0,v);T[idx].maxzd=v;
return idx;
}
void push_up(int x){
if(!x) return;
T[x].s=T[T[x].l].s+T[T[x].r].s+1;
T[x].sum=T[T[x].l].sum+T[T[x].r].sum+T[x].val;
T[x].maxqz=max(max(T[T[x].l].maxqz,T[T[x].l].sum+T[x].val+T[T[x].r].maxqz),0);
T[x].maxhz=max(max(T[T[x].r].maxhz,T[T[x].r].sum+T[x].val+T[T[x].l].maxhz),0);
T[x].maxzd=max(T[x].val,T[x].val+T[T[x].l].maxhz+T[T[x].r].maxqz);
if(T[x].l) T[x].maxzd=max(T[x].maxzd,T[T[x].l].maxzd);
if(T[x].r) T[x].maxzd=max(T[x].maxzd,T[T[x].r].maxzd);
}
void Reverse(int x){
if(!x) return;
swap(T[x].l,T[x].r);
swap(T[x].maxhz,T[x].maxqz);
T[x].lzy^=1;
}
void Cover(int x,int ci){
T[x].val=T[x].cov=ci;T[x].sum=T[x].s*ci;
T[x].maxqz=T[x].maxhz=max(0,T[x].sum);
T[x].maxzd=max(ci,T[x].sum);
T[x].cov_flg=1;
}
void push_down(int x){
if(!x) return;
if(T[x].lzy){
if(T[x].l) Reverse(T[x].l);
if(T[x].r) Reverse(T[x].r);
T[x].lzy=0;
}
if(T[x].cov_flg){
if(T[x].l) Cover(T[x].l,T[x].cov);
if(T[x].r) Cover(T[x].r,T[x].cov);
T[x].cov=T[x].cov_flg=0;
}
}
void Del(int x){
if(!x) return;
stk[++len]=x;
if(T[x].l) Del(T[x].l);if(T[x].r) Del(T[x].r);
}
void Split(int x,int &L,int &R,int K){
if(x) push_down(x);
if(!x){L=R=0;return;}
if(T[T[x].l].s+1<=K) L=x,Split(T[x].r,T[L].r,R,K-T[T[x].l].s-1);
else R=x,Split(T[x].l,L,T[R].l,K);
push_up(x);
}
void Merge(int x,int y,int &M){
if(!x||!y){M=x+y;return;}
if(90000008%(T[x].s+T[y].s)<T[x].s)
push_down(x),M=x,Merge(T[x].r,y,T[M].r),push_up(x);
else push_down(y),M=y,Merge(x,T[y].l,T[M].l),push_up(y);
}
int N,M;
int A[500005];
int Build(int l,int r){
if(l==r){
return NewNode(A[l]);
}
int x,mid=(l+r)>>1;
Merge(Build(l,mid),Build(mid+1,r),x);
return x;
}
char opt[10];
int main(){
N=read(),M=read();
for(int i=1;i<=500000;++i) stk[++len]=i;
for(int i=1;i<=N;++i) A[i]=read();
Merge(rt,Build(1,N),rt);
while(M--){
scanf("%s",opt);
if(opt[0]=='I'){
int pos=read(),tot=read();
int x,y;
Split(rt,x,y,pos);
for(int i=1;i<=tot;++i) A[i]=read();
Merge(x,Build(1,tot),x);
Merge(x,y,rt);
}
else if(opt[0]=='D'){
int pos=read(),tot=read();
int x,y,z;
Split(rt,x,y,pos-1);
Split(y,y,z,tot);
Del(y);
Merge(x,z,rt);
}
else if(opt[0]=='M'&&opt[2]=='K'){
int pos=read(),tot=read(),ci=read();
int x,y,z;
Split(rt,x,y,pos-1);
Split(y,y,z,tot);
Cover(y,ci);
Merge(x,y,x);
Merge(x,z,rt);
}
else if(opt[0]=='R'){
int pos=read(),tot=read();
int x,y,z;
Split(rt,x,y,pos-1);
Split(y,y,z,tot);
Reverse(y);
Merge(x,y,y);
Merge(y,z,rt);
}
else if(opt[0]=='G'){
int pos=read(),tot=read();
int x,y,z;
Split(rt,x,y,pos-1);
Split(y,y,z,tot);
printf("%d\n",T[y].sum);
Merge(x,y,y);
Merge(y,z,rt);
}
else if(opt[0]=='M'&&opt[2]=='X'){
printf("%d\n",T[rt].maxzd);
}
}
return 0;
}
[JSOI2008]火星人
依旧是维护序列问题。
不同的是我们要维护的是子树\(Hash\)权值和,这个很好维护,而且只有插入删除,我们选择这个替罪羊树。
查询的时候就直接二分查询即可。
比较好写,但是还是差点写自闭了。
[JSOI2008]火星人]
#include<bits/stdc++.h>
#define ll long long
#define N 100005
double alpha = 0.7;
int n;
char si[N + 10];
int bit = 7;
unsigned ll F[N + 10];
unsigned ll hash[N + 10];
int cnt;
int val[N + 10];
int ch[N + 10][2];
int v[N + 10],siz[N + 10];
#define h(x) hash[x]
#define ls(x) ch[x][0]
#define rs(x) ch[x][1]
#define s(x) siz[x]
#define v(x) v[x]
inline int New(int si){++cnt;int x = cnt;h(x) = si;s(x) = 1;v(x) = si;}
int root;
inline void up(int u){
s(u) = s(ls(u)) + s(rs(u)) + 1;
h(u) = h(ls(u)) + v(u) * F[s(ls(u))] + h(rs(u)) * F[s(ls(u)) + 1];
}
inline bool bad(int u){return std::max(s(ls(u)),s(rs(u))) > alpha * s(u);}
inline void dfs(int u,std::vector<int> &v){
if(!u)return ;
dfs(ls(u),v);
v.push_back(u);
dfs(rs(u),v);
}
inline int build(std::vector<int> &v,int l,int r){
if(l >= r)return 0;
#define mid ((l + r) >> 1)
int u = v[mid];
ls(u) = build(v,l,mid);
rs(u) = build(v,mid + 1,r);
up(u);
return u;
}
inline void rebuild(int &u){
std::vector<int>v;
dfs(u,v);
u = build(v,0,(int)v.size());
}
inline void insert(int &u,int x,int si){//在x位置插入一个s字符
// std::cout<<u<<" "<<ls(u)<<" "<<s(ls(u))<<" "<<rs(u)<<" "<<s(rs(u))<<" "<<x<<" "<<si<<std::endl;
if(!u){
New(si);
u = cnt;
return ;
}
if(bad(u))
rebuild(u);
if(x <= s(ls(u)))
insert(ls(u),x,si);
else
insert(rs(u),x - s(ls(u)) - 1,si);
up(u);
}
inline void change(int u,int x,int si){
// std::cout<<u<<" "<<ls(u)<<" "<<s(ls(u))<<" "<<x<<" "<<si<<std::endl;
if(s(ls(u)) + 1 == x)
v(u) = si;
else if(x <= s(ls(u)))change(ls(u),x,si);
else change(rs(u),x - s(ls(u)) - 1,si);
up(u);
}
inline unsigned ll find(int u,int l,int r,int tl,int tr){//...tl...u...tr...
unsigned ll res = 0;
int M = l + s(ls(u));
if(tl <= l && r <= tr){return h(u);}
if(tl <= M - 1 && M - 1 >= l)
res = find(ls(u),l,M - 1,tl,tr);
if(tl <= M && M <= tr)//u点
res = res + v(u) * F[M - std::max(tl,l)];
if(M + 1 <= tr && M + 1 <= r)
res = res + (M + 1 - std::max(tl,l) > 0 ? F[M + 1 - std::max(tl,l)] : 1) * find(rs(u),M + 1,r,tl,tr);
// std::cout<<u<<" "<<l<<" "<<r<<" "<<tl<<" "<<tr<<" "<<M<<" "<<res<<" "<<std::endl;
return res;
}
inline void Dfs(int u){
if(!u)return;
Dfs(ls(u));
// std::cout<<u<<" "<<ls(u)<<" "<<rs(u)<<" "<<v(u)<<" "<<h(u)<<std::endl;
Dfs(rs(u));
}
int len = 0;
inline int solve(int x,int y){
int l = 0,r = std::min(len - x + 1,len - y + 1);
int ans = 0;
#define mid ((l + r) >> 1)
while(l <= r){
if(find(root,1,len,x,x + mid - 1) == find(root,1,len,y,y + mid - 1))
ans = mid,l = mid + 1;
else
r = mid - 1;
}
return ans;
}
int main(){
// freopen("prefix.in","r",stdin);
// freopen("prefix.out","w",stdout);
scanf("%s",si + 1);
len = strlen(si + 1);
F[0] = 1;
for(int i = 1;i < N;++i)
F[i] = F[i - 1] * bit;
for(int i = 1;i <= len;++i)
insert(root,i,si[i] - 'a');
int n;
scanf("%d",&n);
int c = 0;
while(n -- ){
char a,b;
while(a != 'Q' && a != 'R' && a != 'I')
a = getchar();
if(a == 'Q'){
int x,y;
scanf("%d%d",&x,&y);
// std::cout<<x<<" "<<y<<std::endl;
// std::cout<<si + 268<<std::endl;
// std::cout<<si + 446<<std::endl;
// // std::cout<<find(root,1,s(root),x,y)<<std::endl;
std::cout<<solve(x,y)<<std::endl;
// std::cout<<find(root,1,len,446,450)<<std::endl;
}
if(a == 'R'){
int x;
scanf("%d",&x);
while(!(b <= 'z' && b >= 'a'))
b = getchar();
si[x] = b;
change(root,x,b - 'a');
}
if(a == 'I'){
int x;
scanf("%d",&x);
while(!(b <= 'z' && b >= 'a'))
b = getchar();
insert(root,x,b - 'a');
len ++ ;
}
// std::cout<<n<<std::endl;
a = '.';
b = '.';
}
}
[HNOI2011]括号修复 / [JSOI2011]括号序列
其实只有查询需要注意。
我们发现如果两两匹配,那么最后只剩\())))(((((\)这样的类型。
那么我们需要更改的值为 \(\lceil\frac{pre_{max}}{2}\rceil + \lceil\frac{|las_{min}\ |}{2}\rceil\)
至于区间赋值,翻转这些就比较平凡了。
这类有翻转和赋值的操作的维护序列题,建议使用\(fhq_treap\)因为其不需要思考如果进行区间操作,直接分裂即可。
[HNOI2011]括号修复 / [JSOI2011]括号序列]
#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
using namespace std;
int n,m;
int root;
struct node
{
int ls,rs,pmax,pmin,smax,smin,val,rnd,sum,siz;
int retag;
bool swtag;
bool intag;
}tre[100005];
int cnt;
int newnode(int w)
{
cnt++;
tre[cnt].ls=tre[cnt].rs=0;
tre[cnt].rnd=rand();
tre[cnt].intag=tre[cnt].swtag=false;
tre[cnt].val=w;
tre[cnt].pmax=tre[cnt].smax=(w>0?w:0);
tre[cnt].smin=tre[cnt].pmin=(w>0?0:w);
tre[cnt].retag=0;
tre[cnt].sum=w;
tre[cnt].siz=1;
return cnt;
}
void pushup(int x)
{
int ls=tre[x].ls,rs=tre[x].rs;
tre[x].pmax=max(tre[ls].pmax,tre[ls].sum+tre[x].val+tre[rs].pmax);
tre[x].pmin=min(tre[ls].pmin,tre[ls].sum+tre[x].val+tre[rs].pmin);
tre[x].smax=max(tre[rs].smax,tre[rs].sum+tre[x].val+tre[ls].smax);
tre[x].smin=min(tre[rs].smin,tre[rs].sum+tre[x].val+tre[ls].smin);
tre[x].siz=tre[ls].siz+tre[rs].siz+1;
tre[x].sum=tre[ls].sum+tre[rs].sum+tre[x].val;
}
void pushdown(int x)
{
int ls=tre[x].ls,rs=tre[x].rs;
if(tre[x].retag!=0)
{
tre[ls].intag=tre[rs].intag=false;
tre[ls].swtag=tre[rs].swtag=false;
tre[ls].retag=tre[rs].retag=tre[x].retag;
tre[ls].sum=tre[x].retag*tre[ls].siz;
tre[rs].sum=tre[x].retag*tre[rs].siz;
tre[ls].val=tre[rs].val=tre[x].retag;
tre[ls].pmax=tre[ls].smax=(tre[ls].retag>0?tre[ls].retag*tre[ls].siz:0);
tre[rs].pmax=tre[rs].smax=(tre[rs].retag>0?tre[rs].retag*tre[rs].siz:0);
tre[ls].pmin=tre[ls].smin=(tre[ls].retag>0?0:tre[ls].retag*tre[ls].siz);
tre[rs].pmin=tre[rs].smin=(tre[rs].retag>0?0:tre[rs].retag*tre[rs].siz);
tre[x].retag=0;
}
if(tre[x].swtag)
{
tre[ls].swtag^=tre[x].swtag;
tre[rs].swtag^=tre[x].swtag;
swap(tre[ls].pmax,tre[ls].smax);
swap(tre[ls].pmin,tre[ls].smin);
swap(tre[x].ls,tre[x].rs);
swap(ls,rs);
swap(tre[ls].pmax,tre[ls].smax);
swap(tre[ls].pmin,tre[ls].smin);
tre[x].swtag=false;
}
if(tre[x].intag)
{
tre[ls].intag^=tre[x].intag;
tre[rs].intag^=tre[x].intag;
tre[ls].sum=-tre[ls].sum;
tre[rs].sum=-tre[rs].sum;
tre[ls].val=-tre[ls].val;
tre[rs].val=-tre[rs].val;
swap(tre[ls].pmax,tre[ls].pmin);
tre[ls].pmax=-tre[ls].pmax;
tre[ls].pmin=-tre[ls].pmin;
swap(tre[ls].smax,tre[ls].smin);
tre[ls].smax=-tre[ls].smax;
tre[ls].smin=-tre[ls].smin;
swap(tre[rs].pmax,tre[rs].pmin);
tre[rs].pmax=-tre[rs].pmax;
tre[rs].pmin=-tre[rs].pmin;
swap(tre[rs].smax,tre[rs].smin);
tre[rs].smax=-tre[rs].smax;
tre[rs].smin=-tre[rs].smin;
tre[x].intag=false;
}
}
int merge(int x,int y)
{
if(!x||!y) return x^y;
int res=0;
if(tre[x].rnd<tre[y].rnd)
{
res=x;
pushdown(res);
tre[x].rs=merge(tre[x].rs,y);
}
else
{
res=y;
pushdown(res);
tre[y].ls=merge(x,tre[y].ls);
}
pushup(res);
return res;
}
void splitrk(int rt,int &lrt,int &rrt,int k)
{
if(rt==0)
{
lrt=rrt=0;return;
}
pushdown(rt);
int qwq=tre[tre[rt].ls].siz+1;
if(qwq<=k)
{
lrt=rt;
splitrk(tre[rt].rs,tre[lrt].rs,rrt,k-qwq);
}
else
{
rrt=rt;
splitrk(tre[rt].ls,lrt,tre[rrt].ls,k);
}
pushup(rt);
}
void ins(int w)
{
root=merge(root,newnode(w));
}
int main()
{
srand(114514);
scanf("%d%d",&n,&m);
string tmp;
cin>>tmp;
for(int i=1;i<=n;i++)
{
ins((tmp[i-1]==')'?1:-1));
}
for(int i=1;i<=m;i++)
{
string opt;
int x,y;
cin>>opt;
scanf("%d%d",&x,&y);
int rx,ry,rz;
splitrk(root,rx,rz,y);
splitrk(rx,rx,ry,x-1);
if(opt[0]=='R')
{
string qwq;
cin>>qwq;
int val=(qwq[0]==')'?1:-1);
tre[ry].intag=false;
tre[ry].swtag=false;
tre[ry].retag=val;
tre[ry].sum=val*tre[ry].siz;
tre[ry].val=val;
tre[ry].pmax=tre[ry].smax=(tre[ry].retag>0?tre[ry].retag*tre[ry].siz:0);
tre[ry].pmin=tre[ry].smin=(tre[ry].retag>0?0:tre[ry].retag*tre[ry].siz);
}
if(opt[0]=='I')
{
tre[ry].intag^=1;
tre[ry].sum=-tre[ry].sum;
tre[ry].val=-tre[ry].val;
swap(tre[ry].pmax,tre[ry].pmin);
tre[ry].pmax=-tre[ry].pmax;
tre[ry].pmin=-tre[ry].pmin;
swap(tre[ry].smax,tre[ry].smin);
tre[ry].smax=-tre[ry].smax;
tre[ry].smin=-tre[ry].smin;
}
if(opt[0]=='S')
{
tre[ry].swtag^=1;
swap(tre[ry].pmax,tre[ry].smax);
swap(tre[ry].pmin,tre[ry].smin);
}
if(opt[0]=='Q')
{
printf("%d\n",(tre[ry].pmax+1)/2+(-tre[ry].smin+1)/2);
}
root=merge(rx,merge(ry,rz));
}
return 0;
}
[POI2015]LOG
考虑有如下结论:
如果小于\(s\)的和设为\(sum\),大于\(s\)的有\(x\)个:
当\((c - x) * s \leq sum\)时充分必要。
建议观看该题解。
所以直接使用两颗\(BIT\)解决。
[POI2015]LOG
#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
#define lowbit(x) (x & -x)
struct Q{
ll opt,x,y;
}q[1000005];
ll num[1000005],n,l;
ll tree1[1000005],tree2[1000005];
ll nnum[1000005];
void add(ll now,ll p,ll opt){
if(opt == 1){
for(int i = now;i <= l;i += lowbit(i))
tree1[i] += p;
}
if(opt == 2){
for(int i = now;i <= l;i += lowbit(i))
tree2[i] += p;
}
}
ll query(ll now,ll opt){
ll ans = 0;
if(opt == 1){
for(int i = now;i;i -= lowbit(i))
ans += tree1[i];
return ans;
}
if(opt == 2){
for(int i = now;i;i -= lowbit(i))
ans += tree2[i];
return ans;
}
}
ll m;
int main(){
scanf("%lld%lld",&m,&n);
for(int i = 1;i <= n;++i){
char a[10];
scanf("%s",a + 1);
scanf("%lld%lld",&q[i].x,&q[i].y);
if(a[1] == 'U')
q[i].opt = 1;
else
q[i].opt = 2;
num[++num[0]] = q[i].y;
}
std::sort(num + 1,num + 1 + n);
l = std::unique(num + 1,num + 1 + n) - num - 1;
for(int i = 1;i <= n;++i){
if(q[i].opt == 1){
//std::cout<<q[i].y<<" "<<std::lower_bound(num + 1,num + l + 1,q[i].y) - num<<std::endl;
add(std::lower_bound(num + 1,num + l + 1,q[i].y) - num,1,1);
add(std::lower_bound(num + 1,num + l + 1,q[i].y) - num,q[i].y,2);
if(nnum[q[i].x]){
add(std::lower_bound(num + 1,num + l + 1,nnum[q[i].x]) - num,-1,1);
add(std::lower_bound(num + 1,num + l + 1,nnum[q[i].x]) - num,-nnum[q[i].x],2);
}
nnum[q[i].x] = q[i].y;
}
if(q[i].opt == 2){
ll s = query(l,1) - query(std::lower_bound(num + 1,num + l + 1,q[i].y) - num - 1,1);
ll sum = query(std::lower_bound(num + 1,num + l + 1,q[i].y) - num - 1,2);
//std::cout<<s<<" "<<sum<<std::endl;
if(sum >= (q[i].x - s) * q[i].y)
puts("TAK");
else
puts("NIE");
}
}
}
/*
3 8
U 1 5
U 2 7
Z 2 6
U 3 1
Z 2 6
U 2 2
Z 2 6
Z 2 1
*/
[Ynoi2013] 大学
经典结论:考虑每次操作每个数至少减半,所以直接暴力操作即可。
我们要做的就是快速维护 \([l,r]\)中为\(d\)的倍数的数。
直接暴力存下来共有\(nln\)个。
然后考虑并查集优化暴力过程。
\(O(nln(n)\alpha(n) + n log(a) log(n))\)
不想卡常,所以写了个vector版。
[Ynoi2013] 大学
#include<bits/stdc++.h>
#define ll long long
#define N 500005
std::vector<int>Q[N + 100];
std::vector<int>pre[N + 100];
int n,m;
int a[N + 100];
std::vector<int>s[N + 100];
ll T[N];
#define lowbit(x) (x & -x)
inline int find(int x,int u){
return pre[x][u] == u ? u : pre[x][u] = find(x,pre[x][u]);
}
inline void merge(int x,int l,int r){
pre[x][l] = r;
}
inline void add(int x,int p){
for(int i = x;i <= n;i += lowbit(i))
T[i] += p;
}
inline ll q(int x){
ll ans = 0;
for(int i = x;i;i -= lowbit(i))
ans = ans + T[i];
return ans;
}
inline void div(int x,int l,int r){
int now = std::lower_bound(Q[x].begin(),Q[x].end(),l) - Q[x].begin();
while(Q[x][now] <= r){
if(a[Q[x][now]] % x == 0){
ll to = a[Q[x][now]] / x;
add(Q[x][now],to - a[Q[x][now]]);
a[Q[x][now]] /= x;
}
if(a[Q[x][now]] % x != 0 && pre[x][now] == now)
merge(x,now,now + 1);
now = find(x,now + 1);
}
}
int cnt = 2;
int ans[N];
int main(){
for(int i = 1;i < N;++i)
for(int j = 1;j * i < N;++j)
s[i * j].push_back(i);
scanf("%d%d",&n,&m);
for(int i = 1; i<= n;++i){
scanf("%d",&a[i]);
add(i,a[i]);
for(int j = 0;j < s[a[i]].size();++j)
Q[s[a[i]][j]].push_back(i);
}
for(int i = 1;i < N;++i)
Q[i].push_back(n + 1);
for(int i = 1;i < N;++i){
pre[i].resize(Q[i].size() + 10);
for(int j = 0;j < Q[i].size();++j)
pre[i][j] = std::min((int)Q[i].size() - 1,j);
}
while(m -- ){
int opt = 0;
scanf("%d",&opt);
if(opt == 1){
int x,l,r;
scanf("%d%d%d",&l,&r,&x);
int f = ans[cnt - 2];
l ^= f,r ^= f,x ^= f;
div(x,l,r);
}
if(opt == 2){
int l,r;
scanf("%d%d",&l,&r);
std::cout<<(ans[++cnt] = q(r) - q(l - 1))<<std::endl;
}
}
}
[Ynoi2015] 我回来了
思考一下,由于乘上了 \(r - l + 1\) 所以其实等同于 \(\sum_{i = l}^r f(i)\),\(f(i)\)为当前那个序列的初始值为\(i\)的亵渎次数。
我们设\(a_x\)为值为\(x\)的第一次出现的操作数为\(a_x\)。
那么我们设\(t_{d,i} = \min_{l = (i - 1) * d + 1}^{r = id}a_i\)
那么我们设\(f_{d,i}\)为初始值为\(d\),亵渎次数为\(i\)的最少操作数。
所以有\(f_{d,i} = \max(f_{d,i - 1},t_{d,i})\)
可以把\(f_{d,i}\)相同的放在一起,并给他们BIT上加一。
因为一共有 \(nlog\) 个\(t_{d,i}\)。
复杂度为\(O(nlog^2n + mlog)\)
[Ynoi2015] 我回来了
#include<bits/stdc++.h>
#define ll long long
#define N 100010
#define M 1000005
int n,a[N],m,f[N][20],lg[N];
std::pair<int,int>q[M];
std::vector<int>vec[M];
ll T[N];
#define lowbit(x) (x & -x)
inline void add(int x){
for(int i = x;i <= n;i += lowbit(i))
T[i] ++ ;
}
inline int find(int x){
int ans = 0;
for(int i = x;i;i -= lowbit(i))
ans += T[i];
return ans;
}
inline int Q(int l,int r){
int k = lg[r -l + 1];
return std::min(f[l][k],f[r - (1 << k) + 1][k]);
}
int main(){
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;++i)
a[i] = m + 1;
for(int i = 1;i <= m;++i){
int opt;
scanf("%d",&opt);
q[i].first = -1;
if(opt == 1){
int x;
scanf("%d",&x);
a[x] = std::min(a[x],i);
}else
scanf("%d%d",&q[i].first,&q[i].second);
}
for(int i = 2;i <= n;++i)
lg[i] = lg[i >> 1] + 1;
for(int i = 1;i <= n;++i)
f[i][0] = a[i];//ST表
for(int j = 1;j <= 18;++j)
for(int i = 1;i + (1ll << j) - 1 <= n;++i)
f[i][j] = std::min(f[i][j - 1],f[i + (1 << j - 1)][j - 1]);
for(int i = 1;i <= n;++i){
add(i);
int las = 0;
for(int j = 1;j <= n;j += i)
vec[las = std::max(las,Q(j,std::min(j + i - 1,n)))].push_back(i);
}
for(int i = 1;i <= m;++i){
for(int j = 0;j < vec[i].size();++j)
add(vec[i][j]);
if(~q[i].first)
std::cout<<find(q[i].second) - find(q[i].first - 1)<<std::endl;
}
}