[关闭]
@910935974 2019-10-10T11:24:15.000000Z 字数 33421 阅读 517

算法笔记——数据结构

算法 数据结构


  1. #include<stack>
  2. stack<int>stk;

同时也可以用数组模拟栈。

1.进栈:

  1. stk.push(x);

2.出栈(弹出栈顶元素):

  1. stk.pop();

3.检查栈是否非空(空返回0,非空返回1):

  1. stk.empty();

4.输出栈顶元素:

  1. stk.top();

5.输出栈中元素个数:

  1. stk.size();
  1. #include<cstdio>
  2. #define maxn 100005
  3. using namespace std;
  4. int n;
  5. int top,a[maxn],stk[maxn];
  6. int main()
  7. {
  8. scanf("%d",&n);
  9. for(int i=1;i<=n;++i)
  10. scanf("%d",&a[i]);
  11. for(int i=1;i<=n;++i)
  12. {
  13. while(top&&a[i]<stk[top])top--;
  14. stk[++top]=a[i];
  15. }
  16. for(int i=1;i<=top;++i)
  17. printf("%d ",stk[i]);
  18. return 0;
  19. }

队列

  1. #include<queue>
  2. queue<int>que;
  1. queue.push(x);

2.删除:

  1. queue.pop();

3.访问队首元素:

  1. queue.front();

4.访问队尾元素:

  1. queue.back();

5.判断队列是否为空:

  1. queue.empty();

6.询问队列的元素个数:

  1. queue.size();
  1. #include<bits/stdc++.h>
  2. #define M 1000005
  3. using namespace std;
  4. int n,k,a[M],q[M],f[M],f_mx[M];//q数组存下标
  5. int main()
  6. {
  7. scanf("%d%d",&n,&k);
  8. for(int i=1;i<=n;i++)
  9. scanf("%d",&a[i]);
  10. if(k==1)
  11. {
  12. for(int i=1;i<=n;i++)
  13. printf("%d ",a[i]);
  14. printf("\n");
  15. for(int i=1;i<=n;i++)
  16. printf("%d ",a[i]);
  17. return 0;
  18. }
  19. int h=1,t=0;//定义头,尾指针
  20. for(int i=1;i<=n;i++)
  21. {
  22. while(h<=t&&i-q[h]>=k)h++;//判断队首有没有超过区间范围
  23. if(i>=k)f[i-k+1]=min(a[q[h]],a[i]);//求解
  24. while(h<=t&&a[i]<=a[q[t]])t--;//判断队尾是否大于队首
  25. q[++t]=i;
  26. }//求最小值
  27. h=1,t=0;
  28. for(int i=1;i<=n;i++)
  29. {
  30. while(h<=t&&i-q[h]>=k)h++;//判断队首有没有超过区间范围
  31. if(i>=k)f_mx[i-k+1]=max(a[q[h]],a[i]);//求解
  32. while(h<=t&&a[i]>=a[q[t]])t--;//判断队尾是否大于队首
  33. q[++t]=i;
  34. }
  35. for(int i=1;i<=n-k+1;i++)
  36. printf("%d ",f[i]);
  37. printf("\n");
  38. for(int i=1;i<=n-k+1;i++)
  39. printf("%d ",f_mx[i]);
  40. return 0;
  41. }

  1. #include<queue>
  2. priority_queue<int,vector<int>,greater<int> >que;

2.插入:

  1. que.push(x);

3.删除堆顶元素:

  1. que.pop();

4.输出堆顶元素:

  1. que.top();
  1. #include<cstdio>
  2. #include<queue>
  3. #include<algorithm>
  4. using namespace std;
  5. priority_queue<int,vector<int>,greater<int> >que;
  6. int n;
  7. int main()
  8. {
  9. scanf("%d",&n);
  10. for(int i=1;i<=n;++i)
  11. {
  12. int x;
  13. scanf("%d",&x);
  14. if(x==1)
  15. {
  16. int y;
  17. scanf("%d",&y);
  18. que.push(y);
  19. }
  20. if(x==2)
  21. printf("%d\n",que.top());
  22. if(x==3)
  23. que.pop();
  24. }
  25. return 0;
  26. }

2.左偏树:

  1. #include<cstdio>
  2. #include<algorithm>
  3. #define maxn 150005
  4. #define lid tr[x].ch[0]
  5. #define rid tr[x].ch[1]
  6. using namespace std;
  7. struct node
  8. {
  9. int val,dis,fa,ch[2];
  10. }tr[maxn];//定义堆数组
  11. int n,m;
  12. int getfa(int x)
  13. {
  14. if(tr[x].fa!=x)return tr[x].fa=getfa(tr[x].fa);
  15. return x;
  16. }//找根(路径压缩)
  17. int merge(int x,int y)
  18. {
  19. if(x==0||y==0)
  20. return x+y;
  21. if(tr[x].val>tr[y].val||(tr[x].val==tr[y].val&&x>y))
  22. swap(x,y);
  23. rid=merge(rid,y);
  24. if(tr[lid].dis<tr[rid].dis)
  25. swap(lid,rid);
  26. tr[lid].fa=tr[rid].fa=tr[x].fa=x;
  27. tr[x].dis=tr[rid].dis+1;
  28. return x;
  29. }//左偏树的合并
  30. void pop(int x)
  31. {
  32. tr[x].val=-1;
  33. tr[lid].fa=lid;
  34. tr[rid].fa=rid;
  35. tr[x].fa=merge(lid,rid);
  36. }
  37. int main()
  38. {
  39. scanf("%d%d",&n,&m);
  40. tr[0].dis=-1;
  41. for(int i=1;i<=n;++i)
  42. {
  43. scanf("%d",&tr[i].val);
  44. tr[i].fa=i;
  45. }
  46. for(int i=1;i<=m;++i)
  47. {
  48. int op;
  49. scanf("%d",&op);
  50. if(op==1)
  51. {
  52. int x,y;
  53. scanf("%d%d",&x,&y);
  54. if(tr[x].val==-1||tr[y].val==-1)
  55. continue;
  56. if(x==y)
  57. continue;
  58. x=getfa(x);
  59. y=getfa(y);
  60. if(x!=y)
  61. tr[x].fa=tr[y].fa=merge(x,y);
  62. }
  63. else if(op==2)
  64. {
  65. int x;
  66. scanf("%d",&x);
  67. if(tr[x].val==-1)
  68. printf("-1\n");
  69. else
  70. {
  71. int y=getfa(x);
  72. printf("%d\n",tr[y].val);
  73. pop(y);
  74. }
  75. }
  76. }
  77. return 0;
  78. }

ST表

  1. #include<cstdio>
  2. #define maxn 1000005
  3. using namespace std;
  4. int n,m;
  5. int a[maxn],st[maxn][4],log[maxn];
  6. int max(int x,int y)
  7. {
  8. return x>y?x:y;
  9. }
  10. int main()
  11. {
  12. scanf("%d%d",&n,&m);
  13. for(int i=1;i<=n;++i)
  14. scanf("%d",&a[i]);
  15. log[0]=-1;
  16. for(int i=1;i<=n;++i)
  17. {
  18. st[i][0]=a[i];
  19. log[i]=log[i>>1]+1;
  20. }//预处理
  21. for(int j=1;j<=20;++j)
  22. {
  23. for(int i=1;i+(1<<j)-1<=n;++i)
  24. st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
  25. }
  26. for(int i=1;i<=m;++i)
  27. {
  28. int l,r;
  29. scanf("%d%d",&l,&r);
  30. int s=log[r-l+1];
  31. printf("%d\n",max(st[l][s],st[r-(1<<s)+1][s]));
  32. }
  33. return 0;
  34. }

树状数组

1.求lowbit

  1. int lowbit(int n)
  2. {
  3. return n&(-n);
  4. }//求lowbit

2.修改操作

  1. void add(int x,int y)
  2. {
  3. while(x<=n)
  4. {
  5. c[x]+=y;
  6. x+=lowbit(x);
  7. }
  8. }//修改树状数组

3.查询操作

  1. int query(int x)
  2. {
  3. int sum=0;
  4. while(x)
  5. {
  6. sum+=c[x];
  7. x-=lowbit(x);
  8. }
  9. return sum;
  10. }//输出前缀和

完整代码:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int s[500005],c[500005],tot[500005],t,m,n;
  4. int lowbit(int n)
  5. {
  6. return n&(-n);
  7. }//求lowbit
  8. void add(int x,int y)
  9. {
  10. while(x<=n)
  11. {
  12. c[x]+=y;
  13. x+=lowbit(x);
  14. }
  15. }//修改树状数组
  16. int query(int x)
  17. {
  18. int sum=0;
  19. while(x)
  20. {
  21. sum+=c[x];
  22. x-=lowbit(x);
  23. }
  24. return sum;
  25. }//输出前缀和
  26. int main()
  27. {
  28. scanf("%d%d",&n,&m);
  29. for(int i=1;i<=n;i++)
  30. {
  31. scanf("%d",&s[i]);
  32. add(i,s[i]);
  33. }
  34. for(int i=1;i<=m;i++)
  35. {
  36. int a,x,y;
  37. scanf("%d%d%d",&a,&x,&y);
  38. if(a==1)
  39. add(x,y);
  40. if(a==2)
  41. tot[++t]=query(y)-query(x-1);
  42. }
  43. for(int i=1;i<=t;i++)
  44. printf("%d\n",tot[i]);
  45. return 0;
  46. }

支持区间修改的树状数组:差分树状数组
只需要小小的修改:
1.读入数据时:

  1. for(int i=1;i<=n;i++)
  2. scanf("%d",&s1[i]);
  3. for(int i=1;i<=n;i++)
  4. {
  5. s2[i]=s1[i]-s1[i-1];
  6. add(i,s2[i]);
  7. }//建立差分数组

2.区间修改时:

  1. int x,y,z;
  2. scanf("%d%d%d",&x,&y,&z);
  3. add(x,z);
  4. add(y+1,-z);

ok!


线段树

1.普通线段树
a.建树

  1. void build(ll k,ll l,ll r)
  2. {
  3. tr[k].l=l;
  4. tr[k].r=r;
  5. tr[k].flag1=0;
  6. tr[k].flag2=1;
  7. if(l==r)
  8. {
  9. tr[k].sum=a[l];
  10. return;
  11. }
  12. ll mid=(l+r)>>1;
  13. build(lid,l,mid);
  14. build(rid,mid+1,r);
  15. update(k);
  16. }//建树

b.区间加

  1. void modify1(ll k,ll l,ll r,ll del)
  2. {
  3. if(tr[k].l>=l&&tr[k].r<=r)
  4. {
  5. tr[k].sum+=del*(tr[k].r-tr[k].l+1);
  6. tr[k].flag1+=del;
  7. return;
  8. }
  9. ll mid=(tr[k].l+tr[k].r)>>1;
  10. if(tr[k].flag2!=1||tr[k].flag1)
  11. pushdown(k);
  12. if(l<=mid)
  13. modify1(lid,l,r,del);
  14. if(r>mid)
  15. modify1(rid,l,r,del);
  16. update(k);
  17. }//区间加

c.区间乘

  1. void modify2(ll k,ll l,ll r,ll del)
  2. {
  3. if(tr[k].l>=l&&tr[k].r<=r)
  4. {
  5. tr[k].sum*=del;
  6. tr[k].flag1*del;
  7. tr[k].flag2*=del;
  8. return;
  9. }
  10. ll mid=(tr[k].l+tr[k].r)>>1;
  11. if(tr[k].flag2!=1||tr[k].flag1)
  12. pushdown(k);
  13. if(l<=mid)
  14. modify2(lid,l,r,del);
  15. if(r>mid)
  16. modify2(rid,l,r,del);
  17. update(k);
  18. }//区间乘

d.标记下放(难点)

  1. void pushdown(ll k)
  2. {
  3. ll l=tr[k].l,r=tr[k].r;
  4. ll mid=(l+r)>>1;
  5. tr[lid].flag2*=tr[k].flag2;
  6. tr[lid].flag1*=tr[k].flag2;
  7. tr[lid].sum*=tr[k].flag2;
  8. tr[rid].flag2*=tr[k].flag2;
  9. tr[rid].flag1*=tr[k].flag2;
  10. tr[rid].sum*=tr[k].flag2;
  11. tr[k].flag2=1;
  12. tr[lid].flag1+=tr[k].flag1;
  13. tr[rid].flag1+=tr[k].flag1;
  14. tr[lid].sum+=(mid-l+1)*tr[k].flag1;
  15. tr[rid].sum+=(r-mid)*tr[k].flag1;
  16. tr[k].flag1=0;
  17. }

e.区间和查询

  1. ll query(ll k,ll l,ll r)
  2. {
  3. if(tr[k].l>=l&&tr[k].r<=r)
  4. return tr[k].sum;
  5. ll ret=0;
  6. ll mid=(tr[k].l+tr[k].r)>>1;
  7. if(tr[k].flag2!=1||tr[k].flag1)
  8. pushdown(k);
  9. if(l<=mid)
  10. ret+=query(lid,l,r);
  11. if(r>mid)
  12. ret+=query(rid,l,r);
  13. return ret;
  14. }//区间求和

f.所谓的上传(update)

  1. void update(ll k)
  2. {
  3. tr[k].sum=tr[lid].sum+tr[rid].sum;
  4. }

完整代码:

  1. #include<cstdio>
  2. #define ll long long
  3. #define maxn 1000005
  4. #define lid (k<<1)
  5. #define rid (k<<1|1)
  6. using namespace std;
  7. struct node
  8. {
  9. ll l,r;
  10. ll flag1,flag2;
  11. ll sum;
  12. }tr[maxn*4];
  13. ll n,m,mod;
  14. ll a[maxn];
  15. void update(ll k)
  16. {
  17. tr[k].sum=tr[lid].sum+tr[rid].sum;
  18. }
  19. void pushdown(ll k)
  20. {
  21. ll l=tr[k].l,r=tr[k].r;
  22. ll mid=(l+r)>>1;
  23. tr[lid].flag2*=tr[k].flag2;
  24. tr[lid].flag1*=tr[k].flag2;
  25. tr[lid].sum*=tr[k].flag2;
  26. tr[rid].flag2*=tr[k].flag2;
  27. tr[rid].flag1*=tr[k].flag2;
  28. tr[rid].sum*=tr[k].flag2;
  29. tr[k].flag2=1;
  30. tr[lid].flag1+=tr[k].flag1;
  31. tr[rid].flag1+=tr[k].flag1;
  32. tr[lid].sum+=(mid-l+1)*tr[k].flag1;
  33. tr[rid].sum+=(r-mid)*tr[k].flag1;
  34. tr[k].flag1=0;
  35. }
  36. void build(ll k,ll l,ll r)
  37. {
  38. tr[k].l=l;
  39. tr[k].r=r;
  40. tr[k].flag1=0;
  41. tr[k].flag2=1;
  42. if(l==r)
  43. {
  44. tr[k].sum=a[l];
  45. return;
  46. }
  47. ll mid=(l+r)>>1;
  48. build(lid,l,mid);
  49. build(rid,mid+1,r);
  50. update(k);
  51. }//建树
  52. void modify1(ll k,ll l,ll r,ll del)
  53. {
  54. if(tr[k].l>=l&&tr[k].r<=r)
  55. {
  56. tr[k].sum+=del*(tr[k].r-tr[k].l+1);
  57. tr[k].flag1+=del;
  58. return;
  59. }
  60. ll mid=(tr[k].l+tr[k].r)>>1;
  61. if(tr[k].flag2!=1||tr[k].flag1)
  62. pushdown(k);
  63. if(l<=mid)
  64. modify1(lid,l,r,del);
  65. if(r>mid)
  66. modify1(rid,l,r,del);
  67. update(k);
  68. }//区间加
  69. void modify2(ll k,ll l,ll r,ll del)
  70. {
  71. if(tr[k].l>=l&&tr[k].r<=r)
  72. {
  73. tr[k].sum*=del;
  74. tr[k].flag1*del;
  75. tr[k].flag2*=del;
  76. return;
  77. }
  78. ll mid=(tr[k].l+tr[k].r)>>1;
  79. if(tr[k].flag2!=1||tr[k].flag1)
  80. pushdown(k);
  81. if(l<=mid)
  82. modify2(lid,l,r,del);
  83. if(r>mid)
  84. modify2(rid,l,r,del);
  85. update(k);
  86. }//区间乘
  87. ll query(ll k,ll l,ll r)
  88. {
  89. if(tr[k].l>=l&&tr[k].r<=r)
  90. return tr[k].sum;
  91. ll ret=0;
  92. ll mid=(tr[k].l+tr[k].r)>>1;
  93. if(tr[k].flag2!=1||tr[k].flag1)
  94. pushdown(k);
  95. if(l<=mid)
  96. ret+=query(lid,l,r);
  97. if(r>mid)
  98. ret+=query(rid,l,r);
  99. return ret;
  100. }//区间求和
  101. ll main()
  102. {
  103. scanf("%d%d",&n,&m);
  104. for(ll i=1;i<=n;++i)
  105. scanf("%d",&a[i]);
  106. build(1,1,n);
  107. for(ll i=1;i<=m;++i)
  108. {
  109. ll opt;
  110. ll x,y,k;
  111. scanf("%d",&opt);
  112. switch (opt)
  113. {
  114. case 1:
  115. scanf("%d%d%d",&x,&y,&k);
  116. modify2(1,x,y,k);
  117. break;
  118. case 2:
  119. scanf("%d%d%d",&x,&y,&k);
  120. modify1(1,x,y,k);
  121. break;
  122. case 3:
  123. scanf("%d%d",&x,&y);
  124. printf("%d\n",query(1,x,y));
  125. break;
  126. }
  127. }
  128. return 0;
  129. }

模板题:
a.【模板】线段树 1;
b.【模板】线段树 2

2.可持久化线段树(主席树)
与普通的线段树不同的是,主席树可以询问历史版本,因而在代码上(主要是建树)略有不同。

  1. #include<cstdio>
  2. #define maxn 1000005
  3. using namespace std;
  4. int m,n,ndnum,tot,lc[maxn*24],rc[maxn*24],root[maxn];
  5. int val[maxn],sum[maxn*24],answer[maxn];
  6. void copynode(int a,int b)
  7. {
  8. lc[a]=lc[b],rc[a]=rc[b],sum[a],sum[b];
  9. }
  10. void build(int x,int l,int r)
  11. {
  12. if(l==r)
  13. {
  14. sum[x]=val[l];
  15. return;
  16. }
  17. int mid=(l+r)>>1;
  18. build(lc[x]=++ndnum,l,mid);
  19. build(rc[x]=++ndnum,mid+1,r);
  20. sum[x]=sum[lc[x]]+sum[rc[x]];
  21. }
  22. int modify(int rt,int l,int r,int pos,int del)
  23. {
  24. int t=++ndnum;
  25. copynode(t,rt);
  26. if(l==r)
  27. {
  28. sum[t]=del;
  29. return t;
  30. }
  31. int mid=(l+r)>>1;
  32. if(pos<=mid)
  33. lc[t]=modify(lc[t],l,mid,pos,del);
  34. else
  35. rc[t]=modify(rc[t],mid+1,r,pos,del);
  36. sum[t]=sum[lc[t]]+sum[rc[t]];
  37. return t;
  38. }
  39. int query(int x,int l,int r,int pos)
  40. {
  41. if(l==r)
  42. return sum[x];
  43. int mid=(l+r)>>1;
  44. if(pos<=mid)return query(lc[x],l,mid,pos);
  45. else return query(rc[x],mid+1,r,pos);
  46. }
  47. int main()
  48. {
  49. scanf("%d%d",&n,&m);
  50. for(int i=1;i<=n;i++)
  51. scanf("%d",&val[i]);
  52. build(root[0]=++ndnum,1,n);
  53. for(int i=1;i<=m;i++)
  54. {
  55. int v,stp;
  56. scanf("%d%d",&v,&stp);
  57. if(stp==1)
  58. {
  59. int x;
  60. int y;
  61. scanf("%d%d",&x,&y);
  62. root[i]=modify(root[v],1,n,x,y);
  63. }
  64. if(stp==2)
  65. {
  66. int x;
  67. scanf("%d",&x);
  68. answer[++tot]=query(root[v],1,n,x);
  69. root[i]=root[v];
  70. }
  71. }
  72. for(int i=1;i<=tot;i++)
  73. printf("%d\n",answer[i]);
  74. return 0;
  75. }

模板题:
a.【模板】可持久化线段树 1(主席树)
b.【模板】可持久化数组(可持久化线段树/平衡树)

  1. #include<cstdio>
  2. #define maxn 100005
  3. #define maxm 300005
  4. using namespace std;
  5. int n,m,q,ndnum;
  6. int lc[maxn*24],rc[maxn*24],sum[maxn*24],num[maxn*24];
  7. int root[maxn],fa[maxn];
  8. char op[15];
  9. void update(int k)
  10. {
  11. sum[k]=sum[lc[k]]+sum[rc[k]];
  12. }
  13. int build(int k,int l,int r,int del,int nu)
  14. {
  15. if(!k)k=++ndnum;
  16. if(l==r)
  17. {
  18. num[k]=nu;
  19. sum[k]++;
  20. return k;
  21. }
  22. int mid=(l+r)>>1;
  23. if(del<=mid)
  24. lc[k]=build(lc[k],l,mid,del,nu);
  25. else
  26. rc[k]=build(rc[k],mid+1,r,del,nu);
  27. update(k);
  28. return k;
  29. }//建立值域线段树
  30. int merge(int x,int y,int l,int r)
  31. {
  32. if(!x)return y;
  33. if(!y)return x;
  34. if(x==y)
  35. {
  36. if(num[y])
  37. {
  38. num[x]=num[y];
  39. sum[x]+=sum[y];
  40. }
  41. return x;
  42. }
  43. int mid=(l+r)>>1;
  44. lc[x]=merge(lc[x],lc[y],l,mid);
  45. rc[x]=merge(rc[x],rc[y],mid+1,r);//合并子树
  46. update(x);
  47. return x;
  48. }
  49. int find(int x)
  50. {
  51. if(x!=fa[x])
  52. return fa[x]=find(fa[x]);
  53. return fa[x];
  54. }
  55. void unionn(int x,int y)
  56. {
  57. x=find(x);
  58. y=find(y);
  59. if(x==y)return;
  60. fa[y]=x;//并查集合并
  61. root[x]=merge(root[x],root[y],1,n);//线段树合并
  62. }
  63. int query(int rt,int del,int l,int r)
  64. {
  65. if(sum[rt]<del||!rt)return 0;
  66. if(l==r)return num[rt];
  67. int mid=(l+r)>>1,ret=0;
  68. if(del<=sum[lc[rt]])
  69. ret=query(lc[rt],del,l,mid);
  70. else
  71. ret=query(rc[rt],del-sum[lc[rt]],mid+1,r);
  72. return ret;
  73. }
  74. int main()
  75. {
  76. scanf("%d%d",&n,&m);
  77. for(int i=1;i<=n;++i)
  78. {
  79. int x;
  80. scanf("%d",&x);
  81. fa[i]=i;
  82. root[i]=build(root[i],1,n,x,i);
  83. }//建树
  84. for(int i=1;i<=m;++i)
  85. {
  86. int x,y;
  87. scanf("%d%d",&x,&y);
  88. unionn(x,y);//建桥(合并并查集和线段树)
  89. }
  90. scanf("%d",&q);
  91. for(int i=1;i<=q;++i)
  92. {
  93. int x,y;
  94. scanf("%s",op);
  95. if(op[0]=='B')
  96. {
  97. scanf("%d%d",&x,&y);
  98. unionn(x,y);//建桥
  99. }
  100. else
  101. {
  102. scanf("%d%d",&x,&y);
  103. x=find(x);
  104. int ans=query(root[x],y,1,n);
  105. if(!ans)printf("-1\n");
  106. else printf("%d\n",ans);
  107. }
  108. }
  109. return 0;
  110. }

习题:
1.The Child and Sequence;
2.[SHOI2015]脑洞治疗仪
3.CPU监控;
4.楼房重建


树链剖分

1.建树:

  1. void add(int x,int y)
  2. {
  3. tot++;
  4. tr[tot].v=y;
  5. tr[tot].next=head[x];
  6. head[x]=tot;
  7. }

2.找重儿子:

  1. void dfs1(int x)
  2. {
  3. size[x]=1;
  4. for(int t=head[x];t;t=tr[t].next)
  5. {
  6. int y=tr[t].v;
  7. if(y==fa[x])continue;
  8. fa[y]=x;
  9. dep[y]=dep[x]+1;
  10. dfs1(y);
  11. size[x]+=size[y];
  12. if(size[y]>size[son[x]])
  13. son[x]=y;//找重儿子
  14. }
  15. }//确立父节点、深度,找重孩子

3.求dfs序及当前点走重儿子能走到的最浅的点:

  1. void dfs2(int x,int tp)
  2. {
  3. in[x]=++idc;//dfs序
  4. seq[idc]=x;
  5. top[x]=tp;
  6. if(son[x])dfs2(son[x],tp);
  7. for(int t=head[x];t;t=tr[t].next)
  8. {
  9. int y=tr[t].v;
  10. if(y==fa[x]||y==son[x])continue;
  11. dfs2(y,y);
  12. }
  13. out[x]=idc;
  14. }//寻找当前点走重儿子能走到的最浅的点

4.建立线段树(注意根据的是dfs序建立):

  1. void build(int k,int l,int r)
  2. {
  3. pool[k].l=l;
  4. pool[k].r=r;
  5. if(l==r)
  6. {
  7. pool[k].sum=val[seq[l]];
  8. pool[k].flag=0;
  9. return;
  10. }
  11. int mid=(l+r)>>1;
  12. build(lid,l,mid);
  13. build(rid,mid+1,r);
  14. pool[k].sum=pool[lid].sum+pool[rid].sum;
  15. pool[k].flag=0;
  16. }//建树

5.区间修改(一毛一样)

  1. void modify(int k,int l,int r,int del)
  2. {
  3. if(pool[k].l>=l&&pool[k].r<=r)
  4. {
  5. pool[k].sum+=(pool[k].r-pool[k].l+1)*del;
  6. pool[k].flag+=del;
  7. return;
  8. }
  9. int mid=(pool[k].l+pool[k].r)>>1;
  10. pushdown(k);
  11. if(l<=mid)
  12. modify(lid,l,r,del);
  13. if(r>mid)
  14. modify(rid,l,r,del);
  15. update(k);
  16. }//区间修改

6.区间查询(一毛一样)

  1. int query(int k,int l,int r)
  2. {
  3. if(pool[k].l>=l&&pool[k].r<=r)
  4. return pool[k].sum;
  5. int mid=(pool[k].l+pool[k].r)>>1,ret=0;
  6. pushdown(k);
  7. if(l<=mid)
  8. ret+=query(lid,l,r);
  9. if(r>mid)
  10. ret+=query(rid,l,r);
  11. update(k);
  12. return ret;
  13. }//区间查询

完整代码:

  1. #include<cstdio>
  2. #include<algorithm>
  3. #define ll long long
  4. #define maxn 100005
  5. #define lid (k<<1)
  6. #define rid (k<<1|1)
  7. using namespace std;
  8. struct node
  9. {
  10. int v,next;
  11. }tr[maxn<<1];
  12. struct tree
  13. {
  14. int l,r;
  15. int sum,flag;
  16. }pool[maxn*4];
  17. int n,m,r;
  18. int tot,head[maxn];
  19. int idc,size[maxn],dep[maxn],fa[maxn],son[maxn],top[maxn],in[maxn],out[maxn],seq[maxn];//树链剖分数组
  20. ll val[maxn];
  21. void pushdown(int k)
  22. {
  23. int l,r,mid,flag;
  24. l=pool[k].l;
  25. r=pool[k].r;
  26. flag=pool[k].flag;
  27. mid=(l+r)>>1;
  28. if(!flag)return;
  29. pool[lid].flag+=flag;
  30. pool[lid].sum+=(mid-l+1)*flag;
  31. pool[rid].flag+=flag;
  32. pool[rid].sum+=(r-mid)*flag;
  33. pool[k].flag=0;
  34. }//下放标记
  35. void update(int k)
  36. {
  37. pool[k].sum=pool[lid].sum+pool[rid].sum;
  38. }
  39. void add(int x,int y)
  40. {
  41. tot++;
  42. tr[tot].v=y;
  43. tr[tot].next=head[x];
  44. head[x]=tot;
  45. }
  46. void dfs1(int x)
  47. {
  48. size[x]=1;
  49. for(int t=head[x];t;t=tr[t].next)
  50. {
  51. int y=tr[t].v;
  52. if(y==fa[x])continue;
  53. fa[y]=x;
  54. dep[y]=dep[x]+1;
  55. dfs1(y);
  56. size[x]+=size[y];
  57. if(size[y]>size[son[x]])
  58. son[x]=y;//找重儿子
  59. }
  60. }//确立父节点、深度,找重孩子
  61. void dfs2(int x,int tp)
  62. {
  63. in[x]=++idc;//dfs序
  64. seq[idc]=x;
  65. top[x]=tp;
  66. if(son[x])dfs2(son[x],tp);
  67. for(int t=head[x];t;t=tr[t].next)
  68. {
  69. int y=tr[t].v;
  70. if(y==fa[x]||y==son[x])continue;
  71. dfs2(y,y);
  72. }
  73. out[x]=idc;
  74. }//寻找当前点走重儿子能走到的最浅的点
  75. void build(int k,int l,int r)
  76. {
  77. pool[k].l=l;
  78. pool[k].r=r;
  79. if(l==r)
  80. {
  81. pool[k].sum=val[seq[l]];
  82. pool[k].flag=0;
  83. return;
  84. }
  85. int mid=(l+r)>>1;
  86. build(lid,l,mid);
  87. build(rid,mid+1,r);
  88. pool[k].sum=pool[lid].sum+pool[rid].sum;
  89. pool[k].flag=0;
  90. }//建树
  91. void modify(int k,int l,int r,int del)
  92. {
  93. if(pool[k].l>=l&&pool[k].r<=r)
  94. {
  95. pool[k].sum+=(pool[k].r-pool[k].l+1)*del;
  96. pool[k].flag+=del;
  97. return;
  98. }
  99. int mid=(pool[k].l+pool[k].r)>>1;
  100. pushdown(k);
  101. if(l<=mid)
  102. modify(lid,l,r,del);
  103. if(r>mid)
  104. modify(rid,l,r,del);
  105. update(k);
  106. }//区间修改
  107. int query(int k,int l,int r)
  108. {
  109. if(pool[k].l>=l&&pool[k].r<=r)
  110. return pool[k].sum;
  111. int mid=(pool[k].l+pool[k].r)>>1,ret=0;
  112. pushdown(k);
  113. if(l<=mid)
  114. ret+=query(lid,l,r);
  115. if(r>mid)
  116. ret+=query(rid,l,r);
  117. update(k);
  118. return ret;
  119. }//区间查询
  120. void add1(int x,int y,int z)
  121. {
  122. while(top[x]!=top[y])
  123. {
  124. if(dep[top[x]]<dep[top[y]])swap(x,y);
  125. modify(1,in[top[x]],in[x],z);
  126. x=fa[top[x]];
  127. }
  128. if(dep[x]<dep[y])swap(x,y);
  129. modify(1,in[y],in[x],z);
  130. }//树从x到y结点最短路径上所有节点的值都加上z
  131. int add2(int x,int y)
  132. {
  133. int ret=0;
  134. while(top[x]!=top[y])
  135. {
  136. if(dep[top[x]]<dep[top[y]])swap(x,y);
  137. ret+=query(1,in[top[x]],in[x]);
  138. x=fa[top[x]];
  139. }
  140. if(dep[x]<dep[y])swap(x,y);
  141. ret+=query(1,in[y],in[x]);
  142. return ret;
  143. }//从x到y结点最短路径上所有节点的值之和
  144. void add3(int x,int z)
  145. {
  146. modify(1,in[x],out[x],z);
  147. }//x为根节点的子树内所有节点值都加上z
  148. int add4(int x)
  149. {
  150. return query(1,in[x],out[x]);
  151. }//以x为根节点的子树内所有节点值之和
  152. int main()
  153. {
  154. scanf("%d%d%d",&n,&m,&r);
  155. for(int i=1;i<=n;++i)
  156. scanf("%d",&val[i]);
  157. for(int i=1;i<n;++i)
  158. {
  159. int x,y;
  160. scanf("%d%d",&x,&y);
  161. add(x,y);
  162. add(y,x);
  163. }
  164. dep[r]=1;
  165. fa[r]=0;
  166. dfs1(r);
  167. dfs2(r,r);
  168. build(1,1,idc);
  169. for(int i=1;i<=m;++i)
  170. {
  171. int op,x,y,z;
  172. scanf("%d",&op);
  173. switch (op)
  174. {
  175. case 1:
  176. scanf("%d%d%d",&x,&y,&z);
  177. add1(x,y,z);
  178. break;
  179. case 2:
  180. scanf("%d%d",&x,&y);
  181. printf("%d\n",add2(x,y));
  182. break;
  183. case 3:
  184. scanf("%d%d",&x,&z);
  185. add3(x,z);
  186. break;
  187. case 4:
  188. scanf("%d",&x);
  189. printf("%d\n",add4(x));
  190. break;
  191. }
  192. }
  193. return 0;
  194. }

平衡树

1.Treap平衡树
特点:操作简单,码量小。
缺点:有限制,不能做区间反转等。

a.建树:

  1. struct treap
  2. {
  3. int l,r;
  4. int val,dat;//val是实际权值,dap是随机权值
  5. int size,cnt;//size是子树大小,cnt是元素个数
  6. }tr[maxn];

b.更新:

  1. void update(int p)
  2. {
  3. tr[p].size=tr[tr[p].l].size+tr[tr[p].r].size+tr[p].cnt;
  4. }//更新子树大小

c.建立新节点:

  1. int New(int val)
  2. {
  3. tot++;
  4. tr[tot].val=val;
  5. tr[tot].dat=rand();//赋予随机值
  6. tr[tot].cnt=tr[tot].size=1;
  7. return tot;
  8. }//建立新节点

d.右旋(zig):

  1. void zig(int &p)
  2. {
  3. int q=tr[p].l;
  4. tr[p].l=tr[q].r;
  5. tr[q].r=p;
  6. p=q;
  7. update(tr[p].r);
  8. update(p);
  9. }//右旋操作

e.左旋(zag):

  1. void zag(int &p)
  2. {
  3. int q=tr[p].r;
  4. tr[p].r=tr[q].l;
  5. tr[q].l=p;
  6. p=q;
  7. update(tr[p].l);
  8. update(p);
  9. }//左旋操作

f.建立初始节点(防止越界):

  1. void build()
  2. {
  3. New(-inf),New(inf);
  4. root=1;
  5. tr[1].r=2;
  6. update(root);
  7. }//初始化操作

g.插入节点:

  1. void insert(int &p,int val)
  2. {
  3. if(p==0)
  4. {
  5. p=New(val);
  6. return;
  7. }
  8. if(val==tr[p].val)
  9. {
  10. tr[p].cnt++;
  11. update(p);
  12. return;
  13. }
  14. if(val<tr[p].val)
  15. {
  16. insert(tr[p].l,val);//插入左子树
  17. if(tr[p].dat<tr[tr[p].l].dat)zig(p);//不满足堆的性质,右旋
  18. }
  19. else
  20. {
  21. insert(tr[p].r,val);//插入右子树
  22. if(tr[p].dat<tr[tr[p].r].dat)zag(p);//不满足堆的性质,左旋
  23. }
  24. update(p);
  25. }//插入节点

h.删除节点:

  1. void remove(int &p,int val)
  2. {
  3. if(p==0)return;
  4. if(val==tr[p].val)
  5. {
  6. if(tr[p].cnt>1)
  7. {
  8. tr[p].cnt--;
  9. update(p);
  10. return;
  11. }//有重复
  12. if(tr[p].l||tr[p].r)
  13. {
  14. if(tr[p].r==0||tr[tr[p].l].dat>tr[tr[p].r].dat)
  15. {
  16. zig(p);
  17. remove(tr[p].r,val);
  18. }//空右节点就把当前节点移到右节点
  19. else
  20. {
  21. zag(p);
  22. remove(tr[p].l,val);
  23. }//空左节点就把当前节点移到左节点
  24. update(p);
  25. }//不是叶子节点,向下旋转
  26. else p=0;//没有子树,直接删除
  27. return;
  28. }
  29. val<tr[p].val?remove(tr[p].l,val):remove(tr[p].r,val);
  30. update(p);
  31. return;
  32. }//删除节点

i.求节点的排名:

  1. int getrank(int &p,int val)
  2. {
  3. if(p==0)return 0;
  4. if(val==tr[p].val)return tr[tr[p].l].size+1;//直接返回
  5. if(val<tr[p].val)return getrank(tr[p].l,val);//向左子树查找
  6. return getrank(tr[p].r,val)+tr[tr[p].l].size+tr[p].cnt;//向右子树查找,并加上左子树和当前节点的大小
  7. }//求节点的排名

j.求排名为rank的节点:

  1. int getval(int &p,int rank)
  2. {
  3. if(p==0)return inf;
  4. if(tr[tr[p].l].size>=rank)return getval(tr[p].l,rank);//向左查找
  5. if(tr[tr[p].l].size+tr[p].cnt>=rank)return tr[p].val;//确定排名为rank的节点就是当前节点
  6. return getval(tr[p].r,rank-tr[tr[p].l].size-tr[p].cnt);//向右查找
  7. }//求排名为rank的节点

k.求前驱:

  1. int getpre(int val)
  2. {
  3. int ans=1,p=root;//tr[1].val==-inf
  4. while(p)
  5. {
  6. if(val==tr[p].val)
  7. {
  8. if(tr[p].l>0)
  9. {
  10. p=tr[p].l;
  11. while(tr[p].r>0)p=tr[p].r;
  12. ans=p;
  13. }
  14. break;
  15. }//先向左走一步,再一直向右走
  16. if(tr[p].val<val&&tr[p].val>tr[ans].val)ans=p;//更新最优节点
  17. p=val<tr[p].val?tr[p].l:tr[p].r;
  18. }
  19. return tr[ans].val;
  20. }//求前驱

l.求后继:

  1. int getnext(int val)
  2. {
  3. int p=root,ans=2;//tr[2].val==inf
  4. while(p)
  5. {
  6. if(val==tr[p].val)
  7. {
  8. if(tr[p].r>0)
  9. {
  10. p=tr[p].r;
  11. while(tr[p].l>0)p=tr[p].l;
  12. ans=p;
  13. }//先向右走一步,再一直向左走
  14. break;
  15. }
  16. if(tr[p].val>val&&tr[p].val<tr[ans].val)ans=p;//更新最优节点
  17. p=val<tr[p].val?tr[p].l:tr[p].r;
  18. }
  19. return tr[ans].val;
  20. }//求后继

完整代码:

  1. #include<cstdio>
  2. #include<algorithm>
  3. #define maxn 100005
  4. #define inf 1000000007
  5. using namespace std;
  6. struct treap
  7. {
  8. int l,r;
  9. int val,dat;//val是实际权值,dap是随机权值
  10. int size,cnt;//size是子树大小,cnt是元素个数
  11. }tr[maxn];
  12. int n;
  13. int tot,root;
  14. void update(int p)
  15. {
  16. tr[p].size=tr[tr[p].l].size+tr[tr[p].r].size+tr[p].cnt;
  17. }//更新子树大小
  18. int New(int val)
  19. {
  20. tot++;
  21. tr[tot].val=val;
  22. tr[tot].dat=rand();//赋予随机值
  23. tr[tot].cnt=tr[tot].size=1;
  24. return tot;
  25. }//建立新节点
  26. void zig(int &p)
  27. {
  28. int q=tr[p].l;
  29. tr[p].l=tr[q].r;
  30. tr[q].r=p;
  31. p=q;
  32. update(tr[p].r);
  33. update(p);
  34. }//右旋操作
  35. void zag(int &p)
  36. {
  37. int q=tr[p].r;
  38. tr[p].r=tr[q].l;
  39. tr[q].l=p;
  40. p=q;
  41. update(tr[p].l);
  42. update(p);
  43. }//左旋操作
  44. void build()
  45. {
  46. New(-inf),New(inf);
  47. root=1;
  48. tr[1].r=2;
  49. update(root);
  50. }//初始化操作
  51. void insert(int &p,int val)
  52. {
  53. if(p==0)
  54. {
  55. p=New(val);
  56. return;
  57. }
  58. if(val==tr[p].val)
  59. {
  60. tr[p].cnt++;
  61. update(p);
  62. return;
  63. }
  64. if(val<tr[p].val)
  65. {
  66. insert(tr[p].l,val);//插入左子树
  67. if(tr[p].dat<tr[tr[p].l].dat)zig(p);//不满足堆的性质,右旋
  68. }
  69. else
  70. {
  71. insert(tr[p].r,val);//插入右子树
  72. if(tr[p].dat<tr[tr[p].r].dat)zag(p);//不满足堆的性质,左旋
  73. }
  74. update(p);
  75. }//插入节点
  76. void remove(int &p,int val)
  77. {
  78. if(p==0)return;
  79. if(val==tr[p].val)
  80. {
  81. if(tr[p].cnt>1)
  82. {
  83. tr[p].cnt--;
  84. update(p);
  85. return;
  86. }//有重复
  87. if(tr[p].l||tr[p].r)
  88. {
  89. if(tr[p].r==0||tr[tr[p].l].dat>tr[tr[p].r].dat)
  90. {
  91. zig(p);
  92. remove(tr[p].r,val);
  93. }//空右节点就把当前节点移到右节点
  94. else
  95. {
  96. zag(p);
  97. remove(tr[p].l,val);
  98. }//空左节点就把当前节点移到左节点
  99. update(p);
  100. }//不是叶子节点,向下旋转
  101. else p=0;//没有子树,直接删除
  102. return;
  103. }
  104. val<tr[p].val?remove(tr[p].l,val):remove(tr[p].r,val);
  105. update(p);
  106. return;
  107. }//删除节点
  108. int getrank(int &p,int val)
  109. {
  110. if(p==0)return 0;
  111. if(val==tr[p].val)return tr[tr[p].l].size+1;//直接返回
  112. if(val<tr[p].val)return getrank(tr[p].l,val);//向左子树查找
  113. return getrank(tr[p].r,val)+tr[tr[p].l].size+tr[p].cnt;//向右子树查找,并加上左子树和当前节点的大小
  114. }//求节点的排名
  115. int getval(int &p,int rank)
  116. {
  117. if(p==0)return inf;
  118. if(tr[tr[p].l].size>=rank)return getval(tr[p].l,rank);//向左查找
  119. if(tr[tr[p].l].size+tr[p].cnt>=rank)return tr[p].val;//确定排名为rank的节点就是当前节点
  120. return getval(tr[p].r,rank-tr[tr[p].l].size-tr[p].cnt);//向右查找
  121. }//求排名为rank的节点
  122. int getpre(int val)
  123. {
  124. int ans=1,p=root;//tr[1].val==-inf
  125. while(p)
  126. {
  127. if(val==tr[p].val)
  128. {
  129. if(tr[p].l>0)
  130. {
  131. p=tr[p].l;
  132. while(tr[p].r>0)p=tr[p].r;
  133. ans=p;
  134. }
  135. break;
  136. }//先向左走一步,再一直向右走
  137. if(tr[p].val<val&&tr[p].val>tr[ans].val)ans=p;//更新最优节点
  138. p=val<tr[p].val?tr[p].l:tr[p].r;
  139. }
  140. return tr[ans].val;
  141. }//求前驱
  142. int getnext(int val)
  143. {
  144. int p=root,ans=2;//tr[2].val==inf
  145. while(p)
  146. {
  147. if(val==tr[p].val)
  148. {
  149. if(tr[p].r>0)
  150. {
  151. p=tr[p].r;
  152. while(tr[p].l>0)p=tr[p].l;
  153. ans=p;
  154. }//先向右走一步,再一直向左走
  155. break;
  156. }
  157. if(tr[p].val>val&&tr[p].val<tr[ans].val)ans=p;//更新最优节点
  158. p=val<tr[p].val?tr[p].l:tr[p].r;
  159. }
  160. return tr[ans].val;
  161. }//求后继
  162. int main()
  163. {
  164. scanf("%d",&n);
  165. build();
  166. for(int i=1;i<=n;++i)
  167. {
  168. int opt,x;
  169. scanf("%d%d",&opt,&x);
  170. switch(opt)
  171. {
  172. case 1:
  173. insert(root,x);
  174. break;
  175. case 2:
  176. remove(root,x);
  177. break;
  178. case 3:
  179. printf("%d\n",getrank(root,x)-1);
  180. break;
  181. case 4:
  182. printf("%d\n",getval(root,x+1));
  183. break;
  184. case 5:
  185. printf("%d\n",getpre(x));
  186. break;
  187. case 6:
  188. printf("%d\n",getnext(x));
  189. break;
  190. }
  191. }
  192. return 0;
  193. }

模板题:【模板】普通平衡树;

2.Splay平衡树

特点:无左右旋操作,可打标记。
缺点:难理解
a.建树:

  1. struct node
  2. {
  3. int fa,ch[2];
  4. int val,cnt;
  5. int size,rev;
  6. }tr[maxn];//fa是父亲,son是儿子,val为节点的权值,cnt为节点重复的个数,size是节点的子树大小,rev为翻转标记;

b.返回儿子是左儿子还是右儿子:

  1. int get(int x)
  2. {
  3. int f=tr[x].fa;
  4. return x==tr[f].ch[1];
  5. }//返回该节点是左儿子还是右儿子

c.更新子树大小:

  1. void update(int x)
  2. {
  3. int l=tr[x].ch[0],r=tr[x].ch[1];
  4. tr[x].size=tr[x].cnt+tr[l].size+tr[r].size;
  5. }//更新节点的子树大小

d.标记下传(区间翻转操作可用):

  1. void pushdown(int x)
  2. {
  3. if(tr[x].rev)
  4. {
  5. int l=tr[x].ch[0],r=tr[x].ch[1];
  6. swap(tr[x].ch[0],tr[x].ch[1]);
  7. tr[l].rev^=1;
  8. tr[r].rev^=1;
  9. tr[x].rev=0;
  10. }
  11. }//标记下传

e.旋转操作(关键):

  1. void rotate(int x)
  2. {
  3. int f=tr[x].fa,ff=tr[f].fa,k=get(x),s=tr[x].ch[k^1];
  4. tr[f].ch[k]=s;
  5. tr[s].fa=f;
  6. tr[ff].ch[get(f)]=x;
  7. tr[x].fa=ff;
  8. tr[x].ch[k^1]=f;
  9. tr[f].fa=x;
  10. update(x);
  11. update(f);
  12. }//旋转操作

f.Splay操作(关键):

  1. void splay(int x,int k=0)
  2. {
  3. while(tr[x].fa!=k)
  4. {
  5. int f=tr[x].fa,ff=tr[f].fa;
  6. if(ff!=k)
  7. {
  8. if(get(x)==get(f))rotate(f);
  9. else rotate(x);
  10. }
  11. rotate(x);
  12. }
  13. if(!k)root=x;
  14. }//将某一节点旋转到k节点的子节点

g.插入节点(顺便把节点变成根节点):

  1. void insert(int x)
  2. {
  3. int k=root,p=0;
  4. while(k&&tr[k].val!=x)
  5. {
  6. p=k;
  7. k=tr[k].ch[x>tr[k].val];
  8. }
  9. if(k)
  10. tr[k].cnt++;//有该节点了
  11. else
  12. {
  13. k=++tot;
  14. if(p)tr[p].ch[x>tr[p].val]=k;
  15. tr[k].ch[0]=tr[k].ch[1]=0;
  16. tr[k].fa=p;
  17. tr[k].val=x;
  18. tr[k].cnt=tr[k].size=1;
  19. }
  20. splay(k);
  21. }//插入节点

h.删除节点:

  1. void remove(int x)
  2. {
  3. int last=getpre(x),next=getnext(x);
  4. splay(last);
  5. splay(next,last);//将x节点移到后继的左子节点
  6. int del=tr[next].ch[0];//提取该节点
  7. if(tr[del].cnt>1)
  8. {
  9. tr[del].cnt--;
  10. splay(del);
  11. }
  12. else
  13. {
  14. tr[next].ch[0]=0;
  15. update(next);
  16. update(root);
  17. }
  18. }//删除

i.find操作:

  1. void find(int x)
  2. {
  3. int k=root;
  4. while(tr[k].ch[x>tr[k].val]&&tr[k].val!=x)
  5. k=tr[k].ch[x>tr[k].val];
  6. splay(k);
  7. }//将最大的小于等于x的数所在的节点splay到根

j.查询排名为x的节点:

  1. int getval(int x)
  2. {
  3. int k=root;
  4. while(1)
  5. {
  6. pushdown(k);
  7. int l=tr[k].ch[0];
  8. if(l&&x<=tr[l].size)
  9. k=tr[k].ch[0];
  10. else if(x>tr[l].size+tr[k].cnt)
  11. {
  12. x-=tr[l].size+tr[k].cnt;
  13. k=tr[k].ch[1];
  14. }
  15. else
  16. return k;
  17. }
  18. }//查询排名x的节点

k.求前驱:

  1. int getpre(int x)
  2. {
  3. find(x);
  4. if(tr[root].val<x)return root;
  5. int k=tr[root].ch[0];
  6. while(tr[k].ch[1])
  7. k=tr[k].ch[1];
  8. return k;
  9. }//求x的前驱

l.求后继:

  1. int getnext(int x)
  2. {
  3. find(x);
  4. if(tr[root].val>x)return root;
  5. int k=tr[root].ch[1];
  6. while(tr[k].ch[0])
  7. k=tr[k].ch[0];
  8. return k;
  9. }//求x的后继

m.区间翻转打标记:

  1. void reverse(int l,int r)
  2. {
  3. int x=getval(l),y=getval(r+2);
  4. splay(x);//将x转到根
  5. splay(y,x);//将y转到x的子节点
  6. tr[tr[y].ch[0]].rev^=1;
  7. }//区间翻转

完整代码:

  1. #include<cstdio>
  2. #include<algorithm>
  3. #define oo 0x3f3f3f3f
  4. #define maxn 100005
  5. using namespace std;
  6. struct node
  7. {
  8. int fa,ch[2];
  9. int val,cnt;
  10. int size,rev;
  11. }tr[maxn];//fa是父亲,son是儿子,val为节点的权值,cnt为节点重复的个数,size是节点的子树大小,rev为翻转标记;
  12. int root,tot;
  13. int n,op,x;
  14. int get(int x)
  15. {
  16. int f=tr[x].fa;
  17. return x==tr[f].ch[1];
  18. }//返回该节点是左儿子还是右儿子
  19. void update(int x)
  20. {
  21. int l=tr[x].ch[0],r=tr[x].ch[1];
  22. tr[x].size=tr[x].cnt+tr[l].size+tr[r].size;
  23. }//更新节点的子树大小
  24. void pushdown(int x)
  25. {
  26. if(tr[x].rev)
  27. {
  28. int l=tr[x].ch[0],r=tr[x].ch[1];
  29. swap(tr[x].ch[0],tr[x].ch[1]);
  30. tr[l].rev^=1;
  31. tr[r].rev^=1;
  32. tr[x].rev=0;
  33. }
  34. }//标记下传
  35. void rotate(int x)
  36. {
  37. int f=tr[x].fa,ff=tr[f].fa,k=get(x),s=tr[x].ch[k^1];
  38. tr[f].ch[k]=s;
  39. tr[s].fa=f;
  40. tr[ff].ch[get(f)]=x;
  41. tr[x].fa=ff;
  42. tr[x].ch[k^1]=f;
  43. tr[f].fa=x;
  44. update(x);
  45. update(f);
  46. }//旋转操作
  47. void splay(int x,int k=0)
  48. {
  49. while(tr[x].fa!=k)
  50. {
  51. int f=tr[x].fa,ff=tr[f].fa;
  52. if(ff!=k)
  53. {
  54. if(get(x)==get(f))rotate(f);
  55. else rotate(x);
  56. }
  57. rotate(x);
  58. }
  59. if(!k)root=x;
  60. }//将某一节点旋转到k节点的子节点
  61. void insert(int x)
  62. {
  63. int k=root,p=0;
  64. while(k&&tr[k].val!=x)
  65. {
  66. p=k;
  67. k=tr[k].ch[x>tr[k].val];
  68. }
  69. if(k)
  70. tr[k].cnt++;//有该节点了
  71. else
  72. {
  73. k=++tot;
  74. if(p)tr[p].ch[x>tr[p].val]=k;
  75. tr[k].ch[0]=tr[k].ch[1]=0;
  76. tr[k].fa=p;
  77. tr[k].val=x;
  78. tr[k].cnt=tr[k].size=1;
  79. }
  80. splay(k);
  81. }//插入节点
  82. void find(int x)
  83. {
  84. int k=root;
  85. while(tr[k].ch[x>tr[k].val]&&tr[k].val!=x)
  86. k=tr[k].ch[x>tr[k].val];
  87. splay(k);
  88. }//将最大的小于等于x的数所在的节点splay到根
  89. int getval(int x)
  90. {
  91. int k=root;
  92. while(1)
  93. {
  94. pushdown(k);
  95. int l=tr[k].ch[0];
  96. if(l&&x<=tr[l].size)
  97. k=tr[k].ch[0];
  98. else if(x>tr[l].size+tr[k].cnt)
  99. {
  100. x-=tr[l].size+tr[k].cnt;
  101. k=tr[k].ch[1];
  102. }
  103. else
  104. return k;
  105. }
  106. }//查询排名x的节点
  107. void reverse(int l,int r)
  108. {
  109. int x=getval(l),y=getval(r+2);
  110. splay(x);//将x转到根
  111. splay(y,x);//将y转到x的子节点
  112. tr[tr[y].ch[0]].rev^=1;
  113. }//区间翻转
  114. int getpre(int x)
  115. {
  116. find(x);
  117. if(tr[root].val<x)return root;
  118. int k=tr[root].ch[0];
  119. while(tr[k].ch[1])
  120. k=tr[k].ch[1];
  121. return k;
  122. }//求x的前驱
  123. int getnext(int x)
  124. {
  125. find(x);
  126. if(tr[root].val>x)return root;
  127. int k=tr[root].ch[1];
  128. while(tr[k].ch[0])
  129. k=tr[k].ch[0];
  130. return k;
  131. }//求x的后继
  132. void remove(int x)
  133. {
  134. int last=getpre(x),next=getnext(x);
  135. splay(last);
  136. splay(next,last);//将x节点移到后继的左子节点
  137. int del=tr[next].ch[0];//提取该节点
  138. if(tr[del].cnt>1)
  139. {
  140. tr[del].cnt--;
  141. splay(del);
  142. }
  143. else
  144. {
  145. tr[next].ch[0]=0;
  146. update(next);
  147. update(root);
  148. }
  149. }//删除
  150. int main()
  151. {
  152. scanf("%d",&n);
  153. insert(oo);
  154. insert(-oo);
  155. for(int i=1;i<=n;++i)
  156. {
  157. scanf("%d%d",&op,&x);
  158. switch (op)
  159. {
  160. case 1:insert(x);break;
  161. case 2:remove(x);break;
  162. case 3:find(x);printf("%d\n",tr[tr[root].ch[0]].size);break;
  163. case 4:printf("%d\n",tr[getval(x+1)].val);break;
  164. case 5:printf("%d\n",tr[getpre(x)].val);break;
  165. case 6:printf("%d\n",tr[getnext(x)].val);break;
  166. }
  167. }
  168. return 0;
  169. }

模板题:
1.【模板】普通平衡树;
2.【模板】文艺平衡树(Splay);(区间翻转模板题)

1.[TJOI2013]最长上升子序列;
2.[NOI2005]维护数列;


LCT

  1. struct node
  2. {
  3. int fa,size,rev,ch[2];
  4. }tr[maxn];//size记录字数异或和,rev记录翻转标记

2.信息更新:

  1. void update(int x)
  2. {
  3. int l=tr[x].ch[0],r=tr[x].ch[1];
  4. tr[x].size=tr[l].size^tr[r].size^val[x];
  5. }//更新节点的子树异或和

3.标记下传:

  1. void filp(int x)
  2. {
  3. swap(tr[x].ch[0],tr[x].ch[1]);
  4. tr[x].rev^=1;
  5. }
  6. void pushdown(int x)
  7. {
  8. if(tr[x].rev)
  9. {
  10. int l=tr[x].ch[0],r=tr[x].ch[1];
  11. if(l)filp(l);
  12. if(r)filp(r);
  13. tr[x].rev=0;
  14. }
  15. }//标记下传

4.旋转操作:

  1. void rotate(int x)
  2. {
  3. int f=tr[x].fa,ff=tr[f].fa,k=(tr[f].ch[1]==x),s=tr[x].ch[k^1];
  4. if(get(f))tr[ff].ch[tr[ff].ch[1]==f]=x;
  5. if(s)tr[s].fa=f;
  6. tr[f].ch[k]=s;
  7. tr[x].ch[k^1]=f;
  8. tr[x].fa=ff;
  9. tr[f].fa=x;
  10. update(x);
  11. update(f);
  12. }//旋转操作(与splay略有不同)

5.Splay操作:

  1. void splay(int x)
  2. {
  3. int y=x,top=0;
  4. hep[++top]=y;
  5. while(get(y))
  6. {
  7. y=tr[y].fa;
  8. hep[++top]=y;
  9. }
  10. while(top)
  11. pushdown(hep[top--]);
  12. while(get(x))
  13. {
  14. y=tr[x].fa;
  15. top=tr[y].fa;
  16. if(get(y))
  17. {
  18. if((tr[y].ch[0]==x)^(tr[top].ch[0]==y))rotate(x);
  19. else rotate(y);
  20. }
  21. rotate(x);
  22. }
  23. update(x);
  24. return;
  25. }//将x节点旋转到splay中的根

6.将x到树根的路径设为实链

  1. void access(int x)
  2. {
  3. int y=0;
  4. while(x)
  5. {
  6. splay(x);
  7. tr[x].ch[1]=y;
  8. update(x);
  9. y=x;
  10. x=tr[x].fa;
  11. }
  12. }//将x到树根的路径设为实链

7.找树根:

  1. int findroot(int x)
  2. {
  3. access(x);
  4. splay(x);
  5. while(tr[x].ch[0])
  6. {
  7. pushdown(x);
  8. x=tr[x].ch[0];
  9. }
  10. return x;
  11. }//找树根

8.把x设为树根:s

  1. void makeroot(int x)
  2. {
  3. access(x);
  4. splay(x);
  5. filp(x);
  6. }//把x设为树根

9.split操作:

  1. void split(int x,int y)
  2. {
  3. makeroot(x);
  4. access(y);
  5. splay(y);
  6. }

10.动态加边:

  1. void link(int x,int y)
  2. {
  3. makeroot(x);
  4. if(findroot(y)!=x)tr[x].fa=y;
  5. }//x,y建边

11.动态删边:

  1. void cut(int x,int y)
  2. {
  3. makeroot(x);
  4. if(findroot(y)==x&&tr[x].fa==y&&!tr[x].ch[1])
  5. {
  6. tr[x].fa=tr[y].ch[0]=0;
  7. update(y);
  8. }
  9. return;
  10. }//去掉x,y之间的边

完整代码:

  1. #include<cstdio>
  2. #include<algorithm>
  3. #define maxn 300005
  4. #define oo 0x3f3f3f3f
  5. using namespace std;
  6. struct node
  7. {
  8. int fa,size,rev,ch[2];
  9. }tr[maxn];//size记录字数异或和,rev记录翻转标记
  10. int n,m;
  11. int val[maxn],hep[maxn];
  12. int get(int x)
  13. {
  14. int f=tr[x].fa;
  15. return tr[f].ch[0]==x||tr[f].ch[1]==x;
  16. }//返回该节点是左儿子还是右儿子
  17. void update(int x)
  18. {
  19. int l=tr[x].ch[0],r=tr[x].ch[1];
  20. tr[x].size=tr[l].size^tr[r].size^val[x];
  21. }//更新节点的子树异或和
  22. void filp(int x)
  23. {
  24. swap(tr[x].ch[0],tr[x].ch[1]);
  25. tr[x].rev^=1;
  26. }
  27. void pushdown(int x)
  28. {
  29. if(tr[x].rev)
  30. {
  31. int l=tr[x].ch[0],r=tr[x].ch[1];
  32. if(l)filp(l);
  33. if(r)filp(r);
  34. tr[x].rev=0;
  35. }
  36. }//标记下传
  37. void rotate(int x)
  38. {
  39. int f=tr[x].fa,ff=tr[f].fa,k=(tr[f].ch[1]==x),s=tr[x].ch[k^1];
  40. if(get(f))tr[ff].ch[tr[ff].ch[1]==f]=x;
  41. if(s)tr[s].fa=f;
  42. tr[f].ch[k]=s;
  43. tr[x].ch[k^1]=f;
  44. tr[x].fa=ff;
  45. tr[f].fa=x;
  46. update(x);
  47. update(f);
  48. }//旋转操作(与splay略有不同)
  49. void splay(int x)
  50. {
  51. int y=x,top=0;
  52. hep[++top]=y;
  53. while(get(y))
  54. {
  55. y=tr[y].fa;
  56. hep[++top]=y;
  57. }
  58. while(top)
  59. pushdown(hep[top--]);
  60. while(get(x))
  61. {
  62. y=tr[x].fa;
  63. top=tr[y].fa;
  64. if(get(y))
  65. {
  66. if((tr[y].ch[0]==x)^(tr[top].ch[0]==y))rotate(x);
  67. else rotate(y);
  68. }
  69. rotate(x);
  70. }
  71. update(x);
  72. return;
  73. }//将x节点旋转到splay中的根
  74. void access(int x)
  75. {
  76. int y=0;
  77. while(x)
  78. {
  79. splay(x);
  80. tr[x].ch[1]=y;
  81. update(x);
  82. y=x;
  83. x=tr[x].fa;
  84. }
  85. }//将x到树根的路径设为实链
  86. int findroot(int x)
  87. {
  88. access(x);
  89. splay(x);
  90. while(tr[x].ch[0])
  91. {
  92. pushdown(x);
  93. x=tr[x].ch[0];
  94. }
  95. return x;
  96. }//找树根
  97. void makeroot(int x)
  98. {
  99. access(x);
  100. splay(x);
  101. filp(x);
  102. }//把x设为树根
  103. void split(int x,int y)
  104. {
  105. makeroot(x);
  106. access(y);
  107. splay(y);
  108. }
  109. void link(int x,int y)
  110. {
  111. makeroot(x);
  112. if(findroot(y)!=x)tr[x].fa=y;
  113. }//x,y建边
  114. void cut(int x,int y)
  115. {
  116. makeroot(x);
  117. if(findroot(y)==x&&tr[x].fa==y&&!tr[x].ch[1])
  118. {
  119. tr[x].fa=tr[y].ch[0]=0;
  120. update(y);
  121. }
  122. return;
  123. }//去掉x,y之间的边
  124. int main()
  125. {
  126. scanf("%d%d",&n,&m);
  127. for(int i=1;i<=n;++i)
  128. scanf("%d",&val[i]);
  129. for(int i=1;i<=m;++i)
  130. {
  131. int op,x,y;
  132. scanf("%d%d%d",&op,&x,&y);
  133. switch (op)
  134. {
  135. case 0:split(x,y);printf("%d\n",tr[y].size);break;
  136. case 1:link(x,y);break;
  137. case 2:cut(x,y);break;
  138. case 3:splay(x);val[x]=y;break;
  139. }
  140. }
  141. return 0;
  142. }

模板题:
1.【模板】Link Cut Tree (动态树)
2.[SDOI2008]洞穴勘测


树套树


并查集

1.查找父亲:

  1. int find(int x)
  2. {
  3. while(father[x]!=x)x=father[x];
  4. return father[x];
  5. }

路径压缩:

  1. int find(int x)
  2. {
  3. if(father[x]!=x)return father[x]=find(father[x]);
  4. return father[x];
  5. }

2.合并集合:

  1. void unionn(int x,int y)
  2. {
  3. x=find(x),y=find(y);
  4. father[y]=x;
  5. }

3.询问两个元素是否在同一个集合中:

  1. int judge(int x,int y)
  2. {
  3. x=find(x),y=find(y);
  4. if(x==y)
  5. return 1;
  6. else return 0;
  7. }

完整代码:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int n,m,q,father[20005],ans[1000005];
  4. int find(int x)
  5. {
  6. while(father[x]!=x)x=father[x];
  7. return father[x];
  8. }
  9. void unionn(int x,int y)
  10. {
  11. x=find(x),y=find(y);
  12. father[y]=x;
  13. }
  14. int judge(int x,int y)
  15. {
  16. x=find(x),y=find(y);
  17. if(x==y)
  18. return 1;
  19. else return 0;
  20. }
  21. int main()
  22. {
  23. scanf("%d%d",&n,&m);
  24. for(int i=1;i<=n;i++)
  25. father[i]=i;
  26. for(int i=1;i<=m;i++)
  27. {
  28. int a,b;
  29. scanf("%d%d",&a,&b);
  30. unionn(a,b);
  31. }
  32. scanf("%d",&q);
  33. for(int i=1;i<=q;i++)
  34. {
  35. int a,b;
  36. scanf("%d%d",&a,&b);
  37. ans[i]=judge(a,b);
  38. }
  39. for(int i=1;i<=q;i++)
  40. if(ans[i]==1)
  41. printf("Yes\n");
  42. else printf("No\n");
  43. return 0;
  44. }

模板题:亲戚
还有一道带权并查集好题:一盘大棋


线段树分治


CDQ分治

  1. #include<cstdio>
  2. #include<algorithm>
  3. #define maxn 200005
  4. using namespace std;
  5. struct node
  6. {
  7. int a,b,c,cnt,ans;
  8. }s1[maxn],s2[maxn];
  9. int n,m,k,mx,top,su[maxn];
  10. int c[maxn];//树状数组
  11. bool cmp1(node x,node y)
  12. {
  13. if(x.a==y.a)
  14. {
  15. if(x.b==y.b)return x.c<y.c;
  16. else return x.b<y.b;
  17. }
  18. else return x.a<y.a;
  19. }//第一维排序
  20. bool cmp2(node x,node y)
  21. {
  22. if(x.b==y.b)
  23. return x.c<y.c;
  24. else return x.b<y.b;
  25. }//第二维排序
  26. int lowbit(int x)
  27. {
  28. return x&(-x);
  29. }
  30. void add(int x,int y)
  31. {
  32. while(x<=mx)
  33. {
  34. c[x]+=y;
  35. x+=lowbit(x);
  36. }
  37. }//树状数组单点加
  38. int query(int x)
  39. {
  40. int sum=0;
  41. while(x)
  42. {
  43. sum+=c[x];
  44. x-=lowbit(x);
  45. }
  46. return sum;
  47. }//求单点前缀和
  48. void cdq(int l,int r)
  49. {
  50. if(l==r)return;
  51. int mid=(l+r)>>1;
  52. cdq(l,mid);
  53. cdq(mid+1,r);//类似于归并排序
  54. sort(s2+l,s2+mid+1,cmp2);
  55. sort(s2+mid+1,s2+r+1,cmp2);//第二维为关键字排序
  56. int i,j=l;
  57. for(i=mid+1;i<=r;++i)
  58. {
  59. while(s2[i].b>=s2[j].b&&j<=mid)
  60. {
  61. add(s2[j].c,s2[j].cnt);//在s2[j]位置加上s2[j]的个数
  62. j++;
  63. }
  64. s2[i].ans+=query(s2[i].c);//保证树状数组里的数一定符合条件
  65. }//类似归并
  66. for(i=l;i<j;++i)
  67. add(s2[i].c,-s2[i].cnt);//清空树状数组
  68. }//cdq分治
  69. int main()
  70. {
  71. scanf("%d%d",&n,&k);
  72. mx=k;//树状数组的区间
  73. for(int i=1;i<=n;++i)
  74. {
  75. int a,b,c;
  76. scanf("%d%d%d",&a,&b,&c);
  77. s1[i].a=a;
  78. s1[i].b=b;
  79. s1[i].c=c;
  80. }//初始化输入
  81. sort(s1+1,s1+1+n,cmp1);//第一维为关键字排序
  82. for(int i=1;i<=n;++i)
  83. {
  84. top++;
  85. if(s1[i].a!=s1[i+1].a||s1[i].b!=s1[i+1].b||s1[i].c!=s1[i+1].c)
  86. {
  87. m++;
  88. s2[m].a=s1[i].a;
  89. s2[m].b=s1[i].b;
  90. s2[m].c=s1[i].c;
  91. s2[m].cnt=top;
  92. top=0;
  93. }
  94. }//第一维已有序,合并相同节点
  95. cdq(1,m);//caq分治
  96. for(int i=1;i<=m;++i)
  97. su[s2[i].ans+s2[i].cnt-1]+=s2[i].cnt;
  98. for(int i=0;i<n;++i)
  99. printf("%d\n",su[i]);
  100. return 0;
  101. }

整体二分


点分治


分块

  1. #include<cstdio>
  2. #include<cmath>
  3. #include<cstring>
  4. #include<algorithm>
  5. #define maxn 50005
  6. #define maxm 305
  7. using namespace std;
  8. struct node
  9. {
  10. int l,r;
  11. }blo[maxm];//记录块的范围
  12. struct tree
  13. {
  14. int past,now,num;
  15. }a[maxn];
  16. int n,m,tot,sum,nw,ans,len,num_all;
  17. int cnt[maxm][maxn],all[maxm][maxn],tong[maxn];//cnt[i][j]表示第i块j(离散化后)的个数,tong[i]表示离散化后i对应的原数
  18. int pos[maxn],wai[maxn],tab[maxn];;//pos[i]表示节点i所在的块
  19. inline int read()
  20. {
  21. char ch;
  22. while((ch=getchar())<'0'||ch>'9');
  23. int res=ch-48;
  24. while((ch=getchar())>='0'&&ch<='9')
  25. res=res*10+ch-48;
  26. return res;
  27. } //读入优化
  28. void print(int x)
  29. {
  30. if(x<0)//负数
  31. {
  32. putchar('-');
  33. x=-x;
  34. }
  35. if(x>9)
  36. print(x/10);
  37. putchar(x%10+'0');
  38. }
  39. bool cmp1(tree x,tree y)
  40. {
  41. return x.past<y.past;
  42. }
  43. bool cmp2(tree x,tree y)
  44. {
  45. return x.num<y.num;
  46. }
  47. int ask(int l,int r)
  48. {
  49. memset(wai,0,sizeof(wai));
  50. int p=pos[l],q=pos[r],ans=0,mx,nww=0;
  51. if(p==q)
  52. {
  53. for(int i=l;i<=r;++i)
  54. wai[a[i].now]++;
  55. for(int i=1;i<=num_all;++i)
  56. {
  57. if(wai[i]>ans)
  58. {
  59. ans=wai[i];
  60. mx=i;
  61. }
  62. }
  63. }//在同一块内
  64. else
  65. {
  66. for(int j=1;j<=num_all;++j)
  67. wai[j]=all[q-1][j]-all[p][j];
  68. for(int i=l;i<=blo[p].r;++i)
  69. wai[a[i].now]++;
  70. for(int i=blo[q].l;i<=r;++i)
  71. wai[a[i].now]++;
  72. for(int i=1;i<=num_all;++i)
  73. {
  74. if(wai[i]>ans)
  75. {
  76. ans=wai[i];
  77. mx=i;
  78. }
  79. }
  80. }
  81. return tab[mx];
  82. }//分块
  83. int main()
  84. {
  85. n=read();m=read();
  86. for(int i=1;i<=n;++i)
  87. {
  88. a[i].past=read();
  89. a[i].num=i;
  90. }
  91. sort(a+1,a+1+n,cmp1);
  92. for(int i=1;i<=n;++i)
  93. {
  94. a[i].now=a[i-1].now;
  95. if(a[i].past!=a[i-1].past)
  96. {
  97. a[i].now++;
  98. num_all++;
  99. tab[num_all]=a[i].past;
  100. }
  101. }//离散化
  102. sort(a+1,a+1+n,cmp2);
  103. tot=sqrt(n);
  104. if(tot*tot<n)tot++;//统计块的个数
  105. for(int i=1;i<=n;i+=tot)
  106. {
  107. blo[++nw].l=i;
  108. blo[nw].r=i+tot-1;
  109. }
  110. if(nw<tot)
  111. {
  112. blo[++nw].l=nw*tot+1;
  113. blo[nw].r=n;
  114. }//统计块的范围
  115. if(blo[nw].r>n)blo[nw].r=n;
  116. for(int i=1;i<=tot;++i)
  117. {
  118. for(int j=blo[i].l;j<=blo[i].r;++j)
  119. {
  120. cnt[i][a[j].now]++;
  121. pos[j]=i;
  122. }
  123. }//预处理出每个块不同数出现的个数
  124. for(int i=1;i<=tot;++i)
  125. {
  126. for(int j=1;j<=num_all;++j)
  127. all[i][j]=all[i-1][j]+cnt[i][j];
  128. }
  129. for(int i=1;i<=m;++i)
  130. {
  131. int l,r;
  132. l=read();r=read();
  133. l=(l+ans-1)%n;
  134. r=(r+ans-1)%n;
  135. l++;r++;
  136. if(l>r)
  137. swap(l,r);
  138. ans=ask(l,r);
  139. print(ans);
  140. putchar('\n');
  141. }
  142. return 0;
  143. }

模板题:
1.蒲公英
2.弹飞绵羊


莫队

  1. #include<cstdio>
  2. #include<cmath>
  3. #include<algorithm>
  4. #define ll long long
  5. #define maxn 50005
  6. using namespace std;
  7. struct node
  8. {
  9. ll l,r,num;
  10. }que[maxn];
  11. struct answer
  12. {
  13. ll mom,son;
  14. }ans[maxn];
  15. ll n,m,tot,mom,sum,total,cntt;
  16. ll co[maxn],son[maxn],cnt[maxn];
  17. bool cmp(node x,node y)
  18. {
  19. return (x.l/tot)^(y.l/tot)?x.l<y.l:((x.l/tot)&1)?x.r<y.r:x.r>y.r;
  20. }//玄学奇偶性排序
  21. ll gcd(ll x,ll y)
  22. {
  23. return (!y)?x:gcd(y,x%y);
  24. }
  25. void add(ll x)
  26. {
  27. if(!x)return;
  28. ll v=co[x];
  29. total+=2*cnt[v];
  30. cnt[v]++;
  31. mom+=2*sum;
  32. sum++;
  33. }
  34. void del(ll x)
  35. {
  36. if(!x)return;
  37. ll v=co[x];
  38. total-=2*cnt[v]-2;
  39. cnt[v]--;
  40. mom-=2*sum-2;
  41. sum--;
  42. }
  43. int main()
  44. {
  45. scanf("%lld%lld",&n,&m);
  46. for(ll i=1;i<=n;++i)
  47. scanf("%lld",&co[i]);
  48. for(ll i=1;i<=m;++i)
  49. {
  50. ll l,r;
  51. scanf("%lld%lld",&l,&r);
  52. if(l==r)
  53. {
  54. ans[i].son=0;
  55. ans[i].mom=1;
  56. continue;
  57. }
  58. cntt++;
  59. que[cntt].l=l;
  60. que[cntt].r=r;
  61. que[cntt].num=i;
  62. }
  63. tot=n/sqrt(cntt*2/3);
  64. sort(que+1,que+1+cntt,cmp);
  65. ll l=0,r=0;
  66. for(ll i=1;i<=m;++i)
  67. {
  68. ll ql=que[i].l,qr=que[i].r,nw=que[i].num;
  69. while(l<ql)del(l++);
  70. while(l>ql)add(--l);
  71. while(r<qr)add(++r);
  72. while(r>qr)del(r--);
  73. ans[nw].mom=mom;
  74. ans[nw].son=total;
  75. }
  76. for(ll i=1;i<=m;++i)
  77. {
  78. ll r=gcd(ans[i].son,ans[i].mom);
  79. ans[i].son/=r;
  80. ans[i].mom/=r;
  81. printf("%lld/%lld\n",ans[i].son,ans[i].mom);
  82. }
  83. return 0;
  84. }

模板题: [国家集训队]小Z的袜子

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注