# 欢迎使用Markdown编辑器写博客
题解:莫队算法模板题!
当然也可以用线段树解决:
离线+ 线段树
将询问R 值排序,考虑维护一个数组A,表示当前询问R 值确定,对于每
一个L 值答案是多少
假设一直R-1 时的数组A’,我们需要求R 的数组A
预处理处每个位置相同值的上一次出现位置prv[i]。
那么对于A 数组中所有prv[r]+1 到r 的位置,都要加v[r],因为这些位置
到r 的区间v[r] 只出现过1 次。
同理prv[prv[r]]+1 到prv[r] 需要-v[r]
同理prv[prv[prv[r]]]+1 到prv[prv[r]] 需要+3*v[r]
同理1 到prv[prv[prv[r]]] 需要+v[r]
区间修改单点查询,线段树或树状数组都可以。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const int MAXN=1e5+4;
int n,m,siz;
int cnt[MAXN],num[MAXN],bel[MAXN];
ll ans[MAXN],ret=0;
struct Q {
int l,r,id;
friend bool operator <(const Q &x,const Q &y) {
return bel[x.l]==bel[y.l]?x.r<y.r:x.l<y.l;
}
}q[MAXN];
inline int read() {
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x*f;
}
int main() {
freopen("abnormal.in","r",stdin);
freopen("abnormal.out","w",stdout);
n=read(),m=read(),siz=(int)sqrt((double)n);
for (register int i=1;i<=n;++i) num[i]=read(),bel[i]=i/siz;
for (register int i=1;i<=m;++i) q[i].l=read(),q[i].r=read(),q[i].id=i;
sort(q+1,q+m+1);
for (register int i=1,l=1,r=0;i<=m;++i) {
while (l>q[i].l) {
++cnt[num[--l]];
ret+=num[l];
if (cnt[num[l]]==3) ret+=(num[l]<<1);
if (cnt[num[l]]==2) ret-=(num[l]<<1);
}
while (r<q[i].r) {
++cnt[num[++r]];
ret+=num[r];
if (cnt[num[r]]==3) ret+=(num[r]<<1);
if (cnt[num[r]]==2) ret-=(num[r]<<1);
}
while (l<q[i].l) {
--cnt[num[l++]];
ret-=num[l-1];
if (cnt[num[l-1]]==1) ret+=(num[l-1]<<1);
if (cnt[num[l-1]]==2) ret-=(num[l-1]<<1);
}
while (r>q[i].r) {
--cnt[num[r--]];
ret-=num[r+1];
if (cnt[num[r+1]]==1) ret+=(num[r+1]<<1);
if (cnt[num[r+1]]==2) ret-=(num[r+1]<<1);
}
ans[q[i].id]=ret;
}
for (register int i=1;i<=m;++i) printf("%I64d\n",ans[i]);
return 0;
}
题解:trie+ 启发式合并
首先,预处理处每个节点到根节点路径异或和v[x]
则x,y 路径异或和为v[x] xor v[y] xor a[lca(x,y)]
其次,我们可以通过trie 树贪心来O(log) 求出一个集合和一个数的异或最
大值。
最后,集合的合并如果每次都将小的合并到大的上面去,复杂度O(nlogn),
名叫启发式合并
于是这道题我们就可以用trie 维护子树v 值的集合,并通过启发式合并向
上进行。每次贪心找最大路径异或和
题解2:树链剖分+可持久化Trie
大概就是用双指针扫当前子树对应的dfs序区间,每次处理后r就往后移,而起点l不动,相当于就是将处理过的启发式合并!由于时间复杂度限制,需要树链剖分来优化对重儿子的处理。由于这样的优化只能用一次,所以肯定选size最大的儿子即重儿子来优化。为什么只能用一次呢,因为只有第一次Trie_query是第1个儿子区间与当前根的询问,后面就是第i个儿子与(根+第1个儿子区间+第2个儿子区间+…+第i-1个儿子区间)询问。
我估计我说了还是没几个人看得懂,所以附上大佬的博客地址orz
戳这里看大佬的原创解法
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<utility>
using namespace std;
#define pii pair<int,int>
#define mp(x,y) make_pair(x,y)
const int MAXN=1e5+4;
int n,head[MAXN],edge=0;
struct EDGE {
int v,nxt;
}e[MAXN<<1];
int siz[MAXN],in[MAXN],out[MAXN],rk[MAXN],son[MAXN],fa[MAXN],dis[MAXN],tim=0;
int a[MAXN],seq[MAXN];
inline int read() {
int x=0;char c=getchar();
while (c<'0'||c>'9') c=getchar();
while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x;
}
inline void adde(int u,int v) {
e[edge].nxt=head[u],e[edge].v=v,head[u]=edge++;
e[edge].nxt=head[v],e[edge].v=u,head[v]=edge++;
}
/*----------DCP----------*/
void dfs1(int p,int f) {
siz[p]=1,fa[p]=f;
for (int i=head[p];~i;i=e[i].nxt) {
int v=e[i].v;
if (v^f) {
dis[v]=dis[p]^a[v];
dfs1(v,p);
siz[p]+=siz[v];
if (son[p]==-1||siz[son[p]]<siz[v]) son[p]=v;
}
}
}
void dfs2(int p) {
in[p]=++tim,rk[in[p]]=p;
seq[tim]=dis[p];
if (son[p]==-1) {out[p]=tim;return ;}
dfs2(son[p]);//保证先dfs重儿子
for (int i=head[p];~i;i=e[i].nxt) {
int v=e[i].v;
if (v^fa[p]&&v^son[p])
dfs2(v);
}
out[p]=tim;
}
/*----------Persistent Trie----------*/
struct TRIE {
int son[2],w;
TRIE() {w=0;}
}t[MAXN*33];
int tot=0,root[MAXN];
void Insert(int pre,int &rt,int d,int step) {
t[rt=++tot]=t[pre];
++t[rt].w;
if (step<0) return ;
int p=(d>>step)&1;
Insert(t[pre].son[p],t[rt].son[p],d,step-1);
}
int query(int d,int pre,int rt,int step) {
if (step<0) return 0;
int p=(d>>step)&1;
if (t[t[rt].son[!p]].w-t[t[pre].son[!p]].w)
return (1<<step)+query(d,t[pre].son[!p],t[rt].son[!p],step-1);
else return query(d,t[pre].son[p],t[rt].son[p],step-1);
}
int main() {
freopen("irregular.in","r",stdin);
freopen("irregular.out","w",stdout);
memset(head,-1,sizeof(head));
memset(son,-1,sizeof(son));
n=read();
for (register int i=1;i<=n;++i) a[i]=read();
for (register int i=1;i<n;++i) {
int u=read(),v=read();
adde(u,v);
}
dis[1]=a[1];
dfs1(1,0);
dfs2(1);
// for (int i=1;i<=n;++i) printf("%d %d %d %d\n",in[i],out[i],dis[i],seq[i]);
for (register int i=1;i<=tim;++i) Insert(root[i-1],root[i],seq[i],31);
int l,r,v;
for (register int i=1;i<=n;++i) {
int ans=a[i];
if (fa[rk[in[i]+1]]==i) {
v=son[rk[in[i]]];//=rk[in[i]+1]
l=in[v],r=out[v];
ans=max(ans,query(seq[in[i]]^a[i],root[l-1],root[r],31));
l=in[i];
}
else {
printf("%d ",ans);
continue;
}
while (fa[rk[out[v]+1]]==i) {
v=rk[out[v]+1];
for (int p=in[v];p<=out[v];++p)
ans=max(ans,query(seq[p]^a[i],root[l-1],root[r],31));
r=out[v];
}
printf("%d ",ans);
}
return 0;
}
题解:双堆维护动态中位数,本来大部分平衡树是可以实现的但是被卡了,于是有些大佬就写了RBT,orzorzorz。
双堆实现:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;
const int MAXN=1e6+4,MOD=1e9+7;
int a,b,c,n,f;
ll ret=1;
priority_queue<int, vector<int>, less<int> > qmax;//bigger root
priority_queue<int, vector<int>, greater<int> > qmin;//smaller root
inline void add(int x) {
if (qmin.empty()) {qmin.push(x);return ;}
if (x>qmin.top()) qmin.push(x);
else qmax.push(x);
if (qmin.size()<qmax.size()) {//while
qmin.push(qmax.top());
qmax.pop();
}
if (qmin.size()>qmax.size()+1) {//while
qmax.push(qmin.top());
qmin.pop();
}
}
int main() {
freopen("unnormal.in","r",stdin);
freopen("unnormal.out","w",stdout);
scanf("%d%d%d%d",&a,&b,&c,&n);
add(1);
for (register int i=2;i<=n;++i) {
add(f);
f=(1ll*a*qmin.top()+1ll*b*i+c)%MOD;
ret+=f;
}
cout<<ret<<endl;
return 0;
}
RBT实现:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=1e6+4,MOD=1e9+7;
int a,b,c,n;
int f[MAXN];
ll ret=1;
struct RBt{
int data,s,c;
bool col;
RBt *fa,*ch[2];
inline void set(int _v,bool _col,int i,RBt *p){
data=_v,col=_col,s=c=i;
fa=ch[0]=ch[1]=p;
}
inline void push_up(){s=ch[0]->s+ch[1]->s+c;}
inline void push_down(){for(RBt *x=this; x->s; x=x->fa)x->s--;}
inline int cmp(int v)const{return data==v?-1:v>data;}
};
struct RedBlackTree{
int top;
RBt *root,*null;
RBt stack[MAXN],*tail,*se[MAXN];
void init(){
tail=&stack[0],null=tail++;
null->set(0,0,0,NULL);
root=null,top=0;
}
inline RBt *newRBt(int v){
RBt *p=null;
if(!top)p=tail++;else p=se[--top];
p->set(v,1,1,null);
return p;
}
inline void rotate(RBt* &x,bool d ){
RBt *y=x->ch[!d];
x->ch[!d]=y->ch[d];
if(y->ch[d]->s)y->ch[d]->fa=x;y->fa=x->fa;
if(!x->fa->s)root=y;else x->fa->ch[x->fa->ch[0]!=x]=y;
y->ch[d]=x,x->fa=y,y->s=x->s,x->push_up();
}
inline void insert(int v){
RBt *x=root,*y=null;
while(x->s){
x->s++,y=x;
int d=x->cmp(v);
if(-1==d){x->c++;return;}
x=x->ch[d];
}
x=newRBt(v);
if(y->s)y->ch[v>y->data]=x;else root=x;
x->fa=y;insert_fix(x);
}
inline void insert_fix(RBt* &x){
while(x->fa->col){
RBt *par=x->fa,*Gp=par->fa;
bool d=par==Gp->ch[0];
RBt *uncle=Gp->ch[d];
if(uncle->col)par->col=uncle->col=0,Gp->col=1,x=Gp;
else if(x==par->ch[d])rotate(x=par,!d);
else Gp->col=1,par->col=0,rotate(Gp,d);
}
root->col=0;
}
inline RBt *find(RBt *x,int data){
while(x->s&&x->data != data)x=x->ch[x->data < data];
return x;
}
inline int kth(int k){
int t;
RBt *x=root;
for(; x->s;){
t=x->ch[0]->s;
if(k<=t)x=x->ch[0];
else if(t+1<=k&&k<=t+x->c)break;
else k-=t+x->c,x=x->ch[1];
}
return x->data;
}
}rbt;
int main() {
freopen("unnormal.in","r",stdin);
freopen("unnormal.out","w",stdout);
scanf("%d%d%d%d",&a,&b,&c,&n);
rbt.init();
rbt.insert(f[1]=1);
for (register int i=2;i<=n;++i) {
int temp=i>>1;
int M=rbt.kth(temp);
f[i]=(1ll*a*M%MOD+1ll*b*i%MOD+c)%MOD;
rbt.insert(f[i]);
ret+=f[i];
}
cout<<ret<<endl;
return 0;
}