高级数据结构模板

线段树
/*
敌兵布阵
HDU - 1166
https://cn.vjudge.net/problem/HDU-1166
题面:线段树裸题
输入:	T组数据 N个工兵营地 N个第i个开始有多少人
			有四种命令 到end结束
			Add i j, i和j为正整数, 表示第i个营地增加j个人(j不超过30)
			Sub i j , i和j为正整数, 表示第i个营地减少j个人(j不超过30);
			Query i j , i和j为正整数, i<=j,表示询问第i到第j个营地的总人数;
样例输入:
			1
			10
			1 2 3 4 5 6 7 8 9 10
			Query 1 3
			Add 3 6
			Query 2 7
			Sub 10 2
			Add 6 3
			Query 3 10
			End
样例输出:
			Case 1:
			6
			33
			59
*/
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define maxn 100000
#define ll long long
ll tree[maxn<<2];
ll num[maxn];
char c[10];
int n;
void update(int now){
	tree[now]=tree[now<<1]+tree[now<<1|1];
}
void build(int l, int r, int now){
	if(l==r){
		tree[now]=num[l];
		return ;
	}
	int mid=(l+r)/2;
	build(l, mid, now<<1);
	build(mid+1, r, now<<1|1);
	update(now);
}
void change(int l, int r, int now, int x, ll c){
	if(l==r){
		tree[now]+=c;
		return;
	}
	int mid=(l+r)/2;
	if(x<=mid) change(l, mid, now<<1, x, c);
	else change(mid+1, r, now<<1|1, x, c);
	update(now);
}
ll seartch(int l, int r, int now, int i, int j){
	if(i<=l&&r<=j) return tree[now];
	ll ans=0;
	int mid=(l+r)/2;
	if(i<=mid) ans+=seartch(l, mid, now<<1, i, j);
	if(j>mid) ans+=seartch(mid+1, r, now<<1|1, i, j);
	return ans;
}
int main(){
	int T;
	scanf("%d",  &T);
	for(int s=1;s<=T;s++){
		printf("Case %d:\n", s);
		scanf("%d",  &n);
		for(int i=1;i<=n;i++){
			scanf("%lld",  &num[i]);
		}
		build(1, n, 1);
		ll a, b;
		while(~scanf("%s",  c)){
			if(c[0]=='E') break;
			scanf("%lld %lld",  &a,  &b);
			if(c[0]=='A') change(1, n, 1, (int)a, b);
			if(c[0]=='S') change(1, n, 1, (int)a, -b);
			if(c[0]=='Q') printf("%lld\n",  seartch(1, n, 1, (int)a, (int)b));
		}
	}
	return 0;
}
线段树+lazy
/*
Just a Hook
HDU - 1698
https://cn.vjudge.net/problem/HDU-1698#author=0
题面:开始都是1,每次将一段区间内的所有数字都变为c
样例输入:
			1
			10
			2
			1 5 2
			5 9 3
样例输出:
			24
解法:线段树+lazy
*/
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define maxn 500000
#define ll long long
ll tree[maxn<<2];
ll lazy[maxn<<2];
int n,m;
void update(int now){
	tree[now]=tree[now<<1]+tree[now<<1|1];
}
void update_lazy(int now,int d){
	if(lazy[now]!=0){
		tree[now<<1]=lazy[now]*(d-d/2);
		tree[now<<1|1]=lazy[now]*(d/2);
		lazy[now<<1]=lazy[now];
		lazy[now<<1|1]=lazy[now];
		lazy[now]=0;
	}
}
void build(int l, int r, int now){
	if(l==r){
		tree[now]=1;
		return ;
	}
	int mid=(l+r)/2;
	build(l, mid, now<<1);
	build(mid+1, r, now<<1|1);
	update(now);
}
void change_lazy(int l, int r, int now, int i, int j, ll c){
	if(i<=l&&r<=j){
		tree[now]=c*(r-l+1);
		lazy[now]=c;
		return;
	}
	update_lazy(now,(r-l+1));
	int mid=(l+r)/2;
	if(i<=mid) change_lazy(l, mid, now<<1, i, j, c);
	if(j>mid) change_lazy(mid+1, r, now<<1|1, i, j, c);
	update(now);
}
ll seartch(int l, int r, int now, int i, int j){
	if(i<=l&&r<=j) return tree[now];
	update_lazy(now,r-l+1);
	ll ans=0;
	int mid=(l+r)/2;
	if(i<=mid) ans+=seartch(l, mid, now<<1, i, j);
	if(j>mid) ans+=seartch(mid+1, r, now<<1|1, i, j);
	return ans;
}
int main(){
  int T,t=1;
  scanf("%d", &T);
  while(T--){
    memset(tree,0,sizeof tree);
    memset(lazy,0,sizeof lazy);
  	scanf("%d %d",  &n, &m);
  	build(1, n, 1);
  	ll a, b, d;
  	while(m--){
      scanf("%lld %lld %lld",  &a,  &b, &d);
  		change_lazy(1, n, 1, (int)a, (int)b, d);
  	}
    printf("Case %d: The total value of the hook is %lld.\n", t++, seartch(1, n, 1, 1, n));
  }
	return 0;
}
#include <bits/stdc++.h>
using namespace std;
#define maxn 510000
struct node{
  int lazy;
  int num;
}tree[maxn];
void update(int now,int d){
  if(tree[now].lazy!=0){
    tree[now<<1].num=tree[now].lazy*(d-d/2);
    tree[now<<1|1].num=tree[now].lazy*(d/2);
    tree[now<<1].lazy=tree[now].lazy;
    tree[now<<1|1].lazy=tree[now].lazy;
    tree[now].lazy=0;
  }
}
void build(int now,int l,int r){
  tree[now].lazy=0;
  if(l==r){
    tree[now].num=1;
    return ;
  }
  int mid=(l+r)>>1;
  build(now<<1,l,mid);
  build(now<<1|1,mid+1,r);
  tree[now].num=tree[now<<1].num+tree[now<<1|1].num;
}
void change(int now,int l,int r,int i,int j,int c){
  if(i<=l&&r<=j){
    tree[now].num=c*(r-l+1);
    tree[now].lazy=c;
    return ;
  }
  update(now,r-l+1);
  int mid=(l+r)>>1;
  if(i<=mid) change(now<<1,l,mid,i,j,c);
  if(j>mid) change(now<<1|1,mid+1,r,i,j,c);
  tree[now].num=tree[now<<1].num+tree[now<<1|1].num;
}
int seartch(int now,int l,int r,int i,int j){
  if(i<=l&&r<=j){
    return tree[now].num;
  }
  int ans=0;
  update(now,r-1+1);
  int mid=(l+r)>>1;
  if(i<=mid) ans+=seartch(now<<1,l,mid,i,j);
  if(j>mid) ans+=seartch(now<<1|1,mid+1,r,i,j);
  return ans;
}
int main(){
  int T,t=1;
  int l,r,k;
  int n,m;
  scanf("%d", &T);
  while(T--){
    scanf("%d %d", &n, &m);
    build(1,1,n);
    while(m--){
      scanf("%d %d %d", &l, &r, &k);
      change(1,1,n,l,r,k);
    }
    printf("Case %d: The total value of the hook is %d.\n", t++, seartch(1,1,n,1,n));
  }
  return 0;
}
线段树+离散化
/*
Mayor's posters
POJ - 2528
https://cn.vjudge.net/problem/POJ-2528#author=_PI_
题意: 贴海报,会覆盖掉之前的点
输入: T组样例 贴海报的次数 海报范围
样例输入:
			1
			5
			1 4
			2 6
			8 10
			3 4
			7 10
样例输出:
			4
解法:
离散化+区间修改
*/
#include <cstring>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define maxn 100000
struct node {
	int l;
	int r;
} num[maxn];
int tree[maxn<<2];
int color[maxn<<1];
int has[10000010];
bool vis[maxn<<1];
int cnt;
void init(){
  memset(tree, 0, sizeof tree);
	memset(vis, false, sizeof vis);
	cnt = 0;
}
void updown(int now){
	if(tree[now] != -1){
		tree[now << 1] = tree[now << 1 | 1] = tree[now];
		tree[now] = -1;
	}
}
void change(int l, int r, int now, int i, int j, int c){
	if(i <= l && r <= j){
		tree[now] = c;
		return ;
	}
	updown(now);
	int mid = (l + r) / 2;
	if(i <= mid) change(l, mid, now<<1, i, j, c);
	if(j > mid) change(mid+1, r, now<<1|1, i, j, c);
}
void seartch(int l, int r, int now, int i, int j){
	if(tree[now] != -1){
		vis[tree[now]] = true;
		return;
	}
	int mid = (l + r) / 2;
	if(i <= mid) seartch(l, mid, now<<1, i, j);
	if(j > mid) seartch(mid+1, r, now<<1|1, i, j);
}
int main() {
	int T, n;
	scanf("%d", &T);
	while(T--){
		init();
		scanf("%d", &n);
		for(int i = 0; i < n; i++) {
			scanf("%d %d", &num[i].l, &num[i].r);
			color[cnt++] = num[i].l;
			color[cnt++] = num[i].r;
		}
		sort(color, color + cnt);
		cnt = unique(color, color + cnt) - color;
		for(int i = 1; i <= cnt; i++) {
			has[color[i - 1]] = i;
		}
		for(int i = 1; i <= n; i++) {
			change(1, cnt, 1, has[num[i - 1].l], has[num[i - 1].r], i);
		}
		seartch(1, cnt, 1, 1, cnt);
		int ans = 0;
		for(int i = 1; i <= n; i++) {
			if(vis[i]){
				ans++;
				//cout<<i<<endl;
			}
		}
		printf("%d\n", ans);
	}

	return 0;
}



/*
Mayor's posters
POJ - 2528
https://cn.vjudge.net/problem/POJ-2528#author=_PI_
题意: 贴海报,会覆盖掉之前的点
输入: T组样例 贴海报的次数 海报范围
样例输入:
			1
			5
			1 4
			2 6
			8 10
			3 4
			7 10
样例输出:
			4
解法:
离散化+区间修改
*/
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define maxn 100000
using namespace std;
struct TREE{
  int l,r;
  bool v;
}tree[maxn<<2];
struct node{
  int l,r;
}num[maxn];
int Hash[10000010];
int color[maxn<<1];
int n;
void build(int l,int r,int now){
  tree[now].l=l;
  tree[now].r=r;
  tree[now].v=false;
  if(l==r) return ;
  int mid=(l+r)>>1;
  build(l,mid,now<<1);
  build(mid+1,r,now<<1|1);
}
bool update(int l,int r,int now){
  if(tree[now].v) return false;
  if(tree[now].l==l&&tree[now].r==r){
    tree[now].v=true;
    return true;
  }
  int temp;
  int mid=(tree[now].l+tree[now].r)>>1;
  if(r<=mid) temp=update(l,r,now<<1);
  else if(l>mid) temp=update(l,r,now<<1|1);
  else{
    bool t1=update(l,mid,now<<1);
    bool t2=update(mid+1,r,now<<1|1);
    temp=t1||t2;
  }
  tree[now].v=tree[now<<1].v&&tree[now<<1|1].v;
  return temp;
}
void init(){
  int cnt=0;
  scanf("%d", &n);
  for(int i=0;i<n;i++){
    scanf("%d %d", &num[i].l, &num[i].r);
    color[cnt++]=num[i].l;
    color[cnt++]=num[i].r;
  }
  sort(color, color+cnt);
  cnt=unique(color,color+cnt)-color;
  
  build(1,cnt,1);
  int ans=0;
  for(int i=n-1;i>=0;i--){
    int l=num[i].l, r=num[i].r;
    if(update(Hash[l], Hash[r], 1)) ans++;
  }
  printf("%d\n", ans);
}
int main(){
  int T;
  scanf("%d", &T);
  while(T--){
    init();
  }
  return 0;
}
可持久化线段树
#include <bits/stdc++.h>
using namespace std;
#define maxn 1000005
int n,m,tot;
int a[maxn],root[maxn];
struct node{
  int l,r,v;
}tree[maxn<<5];
void up(int now){
	tree[now].v=tree[tree[now].l].v+tree[tree[now].r].v;
}
int build_tree(int l,int r){
  int pos=++tot;
  if(l==r){
    tree[pos].v=a[l];
    return pos;
  }
  int mid=l+(r-l)/2;
  tree[pos].l=build_tree(l,mid);
  tree[pos].r=build_tree(mid+1,r);
  up(pos);
  return pos;
}
int update(int now,int l,int r,int tar,int v){
  int pos=++tot;
  if(l==r){
    tree[pos].v=v;
    return pos;
  }
  int mid=l+(r-l)/2;
  tree[pos].l=tree[now].l;
  tree[pos].r=tree[now].r;
  up(pos);
  if(tar<=mid) tree[pos].l=update(tree[now].l,l,mid,tar,v);
  else tree[pos].r=update(tree[now].r,mid+1,r,tar,v);
  return pos;
}
int query(int now,int l,int r,int q){
  if(l==r){
    return now;
  }
  int mid=l+(r-l)/2;
  if(q<=mid) return query(tree[now].l,l,mid,q);
  else return query(tree[now].r,mid+1,r,q);
}
int main(){
  int n,m;
  while(~scanf("%d %d", &n, &m)){
    tot=0;
    for(int i=1;i<=n;i++){
      scanf("%d", &a[i]);
    }
    root[0]=build_tree(1,n);
    int u,v,l,k;
    for(int i=1;i<=m;i++){
      scanf("%d %d %d", &u, &k, &l);
      if(k==1){
        scanf("%d", &v);
        root[i]=update(root[u],1,n,l,v);
      }else{
        printf("%d\n", tree[query(root[u],1,n,l)].v);
        root[i]=root[u];
      }
    }
  }
  return 0;
}

主席树
#include <bits/stdc++.h>
using namespace std;
#define maxn 200010
int n,m,tot;
int a[maxn],b[maxn],root[maxn];
struct node{
  int l,r,v;
}tree[maxn<<5];
int build_tree(int l,int r){
  int pos=++tot;
  tree[pos].v=0;
  if(l==r){
    return pos;
  }
  int mid=l+(r-l)/2;
  tree[pos].l=build_tree(l,mid);
  tree[pos].r=build_tree(mid+1,r);
  return pos;
}
int update(int now,int tar,int l,int r){
  int pos=++tot;
  if(l==r){
    tree[pos].v=tree[now].v+1;
    return pos;
  }
  int mid=l+(r-l)/2;
  tree[pos].l=tree[now].l;
  tree[pos].r=tree[now].r;
  if(tar<=mid) tree[pos].l=update(tree[now].l,tar,l,mid);
  else tree[pos].r=update(tree[now].r,tar,mid+1,r);
  tree[pos].v=tree[tree[pos].l].v+tree[tree[pos].r].v;
  return pos;
}
int query(int now,int last,int k,int l,int r){
  if(l==r){
    return l;
  }
  int mid=l+(r-l)/2;
  int cnt=tree[tree[now].l].v-tree[tree[last].l].v;
  if(k<=cnt) return query(tree[now].l,tree[last].l,k,l,mid);
  else return query(tree[now].r,tree[last].r,k-cnt,mid+1,r);
}
int main(){
  int n,m;
  while(~scanf("%d %d", &n, &m)){
    tot=0;
    for(int i=1;i<=n;i++){
      scanf("%d", &a[i]);
      b[i]=a[i];
    }
    sort(b+1,b+1+n);
    int res=unique(b+1,b+n+1)-(b+1);
    for(int i=1;i<=n;i++){
      a[i]=lower_bound(b+1,b+1+res,a[i])-b;
    }
    root[0]=build_tree(1,res);
    for(int i=1;i<=n;i++){
      root[i]=update(root[i-1],a[i],1,res);
    }
    int l,r,k;
    while(m--){
      scanf("%d %d %d", &l, &r, &k);
      printf("%d\n", b[query(root[r],root[l-1],k,1,res)]);
    }
  }
  return 0;
}

树状数组
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
#define maxn 500010
int n;
int a[maxn];
int t[maxn*4];
struct node{
	int x;
	int id;
}f[maxn];
bool cmp(node a, node b){
	return a.x<b.x;
}
void update(int x,int val){
	while(x<=n){
		t[x]+=val;
		x+=x&(-x);
	}
}
int sum(int x){
	int ans=0;
	while(x>=1){
		ans+=t[x];
		x-=x&(-x);
	}
	return ans;
}
int main(){
	while(~scanf("%d", &n),n){
		for(int i=1;i<=n;i++){
			scanf("%d", &f[i].x);
			f[i].id=i;
		}
		sort(f+1,f+n+1,cmp);
		for(int i=1;i<=n;i++) a[i]=f[i].id;
		memset(t,0,sizeof(t));
		long long ans=0;
		for(int i=1;i<=n;i++){
			update(a[i],1);
			ans+=(i-sum(a[i]));
		}
		printf("%lld\n", ans);
	}
	return 0;
}
树状数组维护差分数组
/*
【模板】树状数组 2
P3368
https://www.luogu.org/problem/P3368
解法: 树状数组维护差分数组
*/
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define maxn 1000000
ll a[maxn];
ll tree[maxn<<1];
ll n,m;
inline long long read()//快读
{
    long long x=0,f=1;char c=getchar();
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c)){x=x*10+c-48;c=getchar();}
    return x*f;
}
void update(int x,int val){
	while(x<=n){
		tree[x]+=val;
		x+=x&(-x);
	}
}
long long sum(long long x){
	long long ans=0;
	while(x>=1){
		ans+=tree[x];
		x-=x&(-x);
	}
	return ans;
}
int main(){
  int s,l,r,x;
  n=read();
  m=read();
  memset(tree,0,(n+1)*sizeof (ll));
  for(int i=1;i<=n;i++){
    a[i]=read();
    update(i,a[i]-a[i-1]);
  }
  while(m--){
    s=read();
    if(s==2){
      x=read();
      printf("%lld\n", sum(x));
    }else{
      l=read();
      r=read();
      x=read();
      update(l,x);
      update(r+1,-x);
    }
  }
}
扫描线
/*
Overlapping Rectangles
2017 ACM-ICPC 亚洲区(南宁赛区)网络赛
https://nanti.jisuanke.com/t/A1282
题面:
      n个矩形,给出左下角的坐标和右上角的坐标,算出n个矩形所覆盖的面积
输入:
      2
      0 0 2 2
      1 1 3 3
      3
      0 0 1 1
      2 2 3 3
      4 4 5 5
      0
输出:
      7
      3
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
#define maxn 5010
struct node{
	int ss;
	double l,r,h;
	node(double l1=0,double r1=0,double h1=0,int ss1=0){
		l=l1;
		r=r1;
		h=h1;
		ss=ss1;
	}
}edge[maxn<<2];
int add[maxn];
double x[maxn<<2],sum[maxn<<2];
bool cmp(node a,node b){
	return a.h<b.h;
}
void pushup(int now,int l,int r){
	if(add[now]) sum[now]=x[r+1]-x[l];
	else if(l==r) sum[now]=0;
	else sum[now]=sum[now<<1]+sum[now<<1|1];
}
void update(int now,int L,int R,int val,int l,int r){
	if(L<=l&&r<=R){
		add[now]+=val;
		pushup(now,l,r);
		return ;
	}
	int mid=(l+r)>>1;
	if(L<=mid) update(now<<1,L,R,val,l,mid);
	if(R>mid) update(now<<1|1,L,R,val,mid+1,r);
	pushup(now,l,r);
}
int main(){
	int n;
	double x1,x2,y1,y2,ans;
	while(cin>>n && n){
		ans=0;
		int top=0,l,r;
		for(int i=0;i<n;i++){
			cin>>x1>>y1>>x2>>y2;
			x[top]=x1;
			edge[top++]=node(x1,x2,y1,1);
			x[top]=x2;
			edge[top++]=node(x1,x2,y2,-1);
		}
		sort(x,x+top);
		sort(edge,edge+top,cmp);
		int k = 1;
		for(int i=1; i<top; i++)
		{
				if(x[i] != x[i-1])
						x[k++] = x[i];
		}
		memset(add,0,sizeof(add));
		memset(sum,0,sizeof(sum));
		for(int i=0; i<top-1; i++){
			l=lower_bound(x,x+k,edge[i].l)-x;
			r=lower_bound(x,x+k,edge[i].r)-x-1;
			update(1,l,r,edge[i].ss,0,k-1);
			ans+=(sum[1]*(edge[i+1].h-edge[i].h));
		}
		cout<<(int)ans<<endl;
	}
	cout<<"*"<<endl;
	return 0;
}

/*
The beautiful values of the palace
The Preliminary Contest for ICPC Asia Nanjing 2019 A.
https://nanti.jisuanke.com/t/41298
题意: 有一个n*n(n为奇数)的矩阵,沿着方阵中心螺旋下降,有m个地方是要建宫殿的,
      宫殿的价值是土地价值的数字总和  例如:123213(1+2+3+2+1+3=12)
      p次询问 问一个区间的所以宫殿价值总和
样例输入:
      1
      3 4 4
      1 1
      2 2
      3 3
      2 3
      1 1 1 1
      2 2 3 3
      1 1 3 3
      1 2 2 3
样例输出:
      5
      18
      23
      17
解法;因为n比较大,所以只能用数学方法计算出要建宫殿的地方的价值
    然后因为p比较大,所以要用到扫描线来处理
*/
#include <bits/stdc++.h>
using namespace std;
#define maxn 1000100
#define ll long long
int n,m,p,cnt;
int tree[maxn];
int sum[maxn],res[maxn];
struct node{
  int f,L,R,h,id;
  int val;
}edge[maxn];
int get(int x, int y){
  long long centre = (long long)n*n;
  long long cen = n/2+1;
  long long val;
  if (x==cen && y==cen){
    int ans=0;
    val=centre;
    while(val){
      ans+=val%10;
      val=val/10;
    }
    return ans;
  }
  long long Max = max(abs(x-cen), abs(y-cen));
  val = centre - Max * (4*Max +1);
  if((y == (cen-Max) && x >= cen) || x == cen + Max) val -= (x-cen) + (y-cen+Max);
  else {
  	if(y != (cen-Max)) val += (x-cen + Max) + (y-cen + Max) + Max;
  	else val += cen - x;
  }
  int ans=0;
  while(val){
    ans+=val%10;
    val=val/10;
  }
  return ans;
}
bool cmp(node a,node b){
  if(a.h==b.h){
    if((a.f==1&&b.f==0)||(b.f==1&&a.f==0)) return a.f>b.f;
    else return a.f<b.f;
  }
  return a.h<b.h;
}
void update(int pow,int val){
  while(pow<maxn){
    tree[pow]+=val;
    pow+=(-pow)&pow;
  }
}
int query(int pow){
  int ans=0;
  while(pow>0){
    ans+=tree[pow];
    pow-=(-pow)&pow;
  }
  return ans;
}
int main(){
  int T;
  int x1,x2,y1,y2,x,y;
  cin>>T;
  while(T--){
    scanf("%d %d %d", &n, &m, &p);
    cnt=0;
    memset(tree,0,sizeof tree);
    while(m--){
      scanf("%d %d", &x, &y);
      edge[cnt++]=(node){0,x,0,y,0,get(x,y)};
    }
    for(int i=1;i<=p;i++){
      scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
      edge[cnt++]=(node){1,x1,x2,y1,i,0};
      edge[cnt++]=(node){2,x1,x2,y2,i,0};
    }
    sort(edge,edge+cnt,cmp);
    for(int i=0;i<cnt;i++){
      if(edge[i].f==0) update(edge[i].L,edge[i].val);
      else if(edge[i].f==1) sum[edge[i].id]=query(edge[i].R)-query(edge[i].L-1);
      else res[edge[i].id]=query(edge[i].R)-query(edge[i].L-1)-sum[edge[i].id];
    }
    for(int i=1;i<=p;i++){
      printf("%d\n", res[i]);
    }
  }
  return 0;
}
01字典树
#include <bits/stdc++.h>
using namespace std;
#define maxn 100000
#define ll long long
int tol;
ll val[32*maxn];
int ch[32*maxn][2];
void init(){
  tol=1;
  ch[0][0]=ch[0][1]=0;
}
void insert(ll x){
  int u=0;
  for(int i=32;i>=0;i--){
    int v=(x>>i)&1;
    if(!ch[u][v]){
      ch[tol][0]=ch[tol][1]=0;
      val[tol]=0;
      ch[u][v]=tol++;
    }
    u=ch[u][v];
  }
  val[u]=x;
}
ll query(ll x){
  int u=0;
  for(int i=32;i>=0;i--){
    int v=(x>>i)&1;
    if(ch[u][v^1]) u=ch[u][v^1];
    else u=ch[u][v];
  }
  return val[u];
}
int main(){
  int T,n,m;
  ll a;
  scanf("%d",&T);
  for(int s=1;s<=T;s++){
    init();
    scanf("%d %d", &n, &m);
    for(int i=0;i<n;i++){
      scanf("%lld", &a);
      insert(a);
    }
    printf("Case #%d:\n", s);
    for(int i=0;i<m;i++){
      scanf("%lld", &a);
      printf("%d\n", query(a));
    }
  }
}

/*
Chip Factory
ACM/ICPC 2015 Changchun(Trie)
https://nanti.jisuanke.com/t/A1941
题面:
有一个数组,让你选取三个数,使其中两个数相加异或上另外一个数值最大。
很明显的异或最大要用01字典树,问题是要选取三个数。我们可以提前把所有的数字插入到01字典树上,
每次枚举其中两个数时,先在字典树上面删除这两个数,然后再查询。问题又来了,我们怎么删除了,因为我们用的是公共前缀,
所以我们可以对01字典树上的边进行计数,每次插入 +1,删除 -1,如此就可以完成删除操作了。
样例输入:
      2
      3
      1 2 3
      3
      100 200 300
样例输出:
      6
      400
*/
#include <bits/stdc++.h>
using namespace std;
#define maxn 1000
#define ll long long
int tol;
ll val[30*maxn];
int ch[30*maxn][2];
int nn[30*maxn][2];
void init(){
  tol=1;
  memset(nn,0,sizeof nn);
  ch[0][0]=ch[0][1]=0;
}
void insert(ll x){
  int u=0;
  for(int i=30;i>=0;i--){
    int v=(x>>i)&1;
    nn[u][v]++;
    if(!ch[u][v]){
      ch[tol][0]=ch[tol][1]=0;
      val[tol]=0;
      ch[u][v]=tol++;
    }
    u=ch[u][v];
  }
  val[u]=x;
}
ll query(ll x){
  int u=0;
  for(int i=30;i>=0;i--){
    int v=(x>>i)&1;
			if(ch[u][v^1]&&nn[u][v^1]) u=ch[u][v^1];
			else u=ch[u][v];
  }
  return val[u];
}
void pp(){
	for(int i=0;i<tol;i++){
		cout<<i<<"  "<<nn[i][0]<<"  "<<nn[i][1]<<endl;
	}
}
ll solve(ll x,ll y){
	ll xx=x,yy=y;
	ll num=x+y;
	ll u=0;
  for(int i=30;i>=0;i--){
		int v=(x>>i)&1;
		nn[u][v]--;
		u=ch[u][v];
	}
	u=0;
	for(int i=30;i>=0;i--){
		int v=(y>>i)&1;
		nn[u][v]--;
		u=ch[u][v];
	}
	ll ans=num^query(num);
	u=0;
	for(int i=30;i>=0;i--){
		int v=(xx>>i)&1;
		nn[u][v]++;
		u=ch[u][v];
	}
	u=0;
	for(int i=30;i>=0;i--){
		int v=(yy>>i)&1;
		nn[u][v]++;
		u=ch[u][v];
	}
	return ans;
}
ll a[maxn];
int main(){
  int T,n,m;
  scanf("%d",&T);
  while(T--){
    init();
    scanf("%d", &n);
    for(int i=0;i<n;i++){
      scanf("%lld", &a[i]);
      insert(a[i]);
    }
		int ans=0,num;
		for(int i=0;i<n;i++){
			for(int j=i+1;j<n;j++){
				num=solve(a[i],a[j]);
				if(ans<num) ans=num;
			}
		}
		cout<<ans<<endl;
  }
}
字典树
#include <bits/stdc++.h>
using namespace std;
struct node {
    int cnt;
    node* child[26];
};
node* root;
void init()
{
    root=new node();
}
void insert(const char s[])
{
    node* now=root;
    for(int i=0;s[i];i++){
        int go=s[i]-'a';
        if(now->child[go]==NULL)
            now->child[go]=new node();
        now=now->child[go];
        now->cnt++;
    }
}
int query(const char s[])
{
    node* now=root;
    for(int i=0;s[i];i++){
        int go=s[i]-'a';
        if(now->child[go]==NULL) return -1;
        now=now->child[go];
    }
    return now->cnt;
}
void del(const char s[])
{
    node* now=root;
    for(int i=0;s[i];i++){
        int go=s[i]-'a';
        if(now->child[go]->cnt==1){
            now->child[go]=NULL;
            return;
        }
        now=now->child[go];
        now->cnt--;
    }
}
int main()
{
    init();
    insert("abcdef");
    insert("abccba");
    cout << query("abc") << endl;
    cout << query("abcc") << endl;
    cout << query("abs") << endl;
    cout << query("abcdef") << endl;
    del("abcdef");
    cout << query("abcdef") << endl;
    cout<<query("abc")<<endl;
    return 0;
}
并查集
#include <bits/stdc++.h>
using namespace std;
#define maxn 100000
int n,m;
int u,v,k;
int fa[maxn];
void init(){
    for(int i=0;i<=n;i++)
        fa[i]=i;
}
int root(int x){
  return fa[x]==x?x:fa[x]=root(fa[x]);
}
void unite(int u,int v){
    u=root(u);
    v=root(v);
    if(u!=v)
        fa[v]=u;
}
bool ailke(int u,int v){
    return root(u)==root(v);
}
int main(){
  cin>>n>>m;
  init();
  for(int i=0;i<m;i++){
    cin>>k>>u>>v;
    if(k==1) unite(u,v);
    if(k==2){
      if(ailke(u,v)){
        cout<<"Y"<<endl;
      }else{
        cout<<"N"<<endl;
      }
    }
  }
  return 0;
}

带权并查集
/*
题面:
      动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。
      现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。
      有人用两种说法对这N个动物所构成的食物链关系进行描述:
      第一种说法是"1 X Y",表示X和Y是同类。
      第二种说法是"2 X Y",表示X吃Y。
      此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。
      当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
      1) 当前的话与前面的某些真的话冲突,就是假话;
      2) 当前的话中X或Y比N大,就是假话;
      3) 当前的话表示X吃X,就是假话。
      你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。
Input
      第一行是两个整数N和K,以一个空格分隔。
      以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
      若D=1,则表示X和Y是同类。
      若D=2,则表示X吃Y。
Output
      只有一个整数,表示假话的数目。
Sample Input
      100 7
      1 101 1 
      2 1 2
      2 2 3 
      2 3 3 
      1 1 3 
      2 3 1 
      1 5 5
Sample Output
      3
*/
//group[0]同类
//group[1]被根吃
//group[2]吃根
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define maxn 1000000
int fa[maxn];
int group[maxn];
int n,m;
int ans;
int d,x,y;
void init(){
  for(int i=0;i<=n;i++){
    fa[i]=i;
    group[i]=0;
  }
  ans=0;
}
int root(int x){
  if(fa[x]==x)return x;
  int r=root(fa[x]);
  group[x]=(group[x]+group[fa[x]])%3;
  return fa[x]=r;
}
int main(){
  scanf("%d%d",&n,&m);
  init();
  while(m--){
    scanf("%d%d%d",&d,&x,&y);
    if(x>n||y>n||(d==2&&x==y)){
      ans++;
      continue;
    }
    int fx=root(x);
    int fy=root(y);
    if(fx==fy){
      if(d==1) if(group[x]!=group[y]) ans++;
      if(d==2) if((3+group[y]-group[x])%3!=1) ans++;
    }else{
      group[fy]=((d-1)+3+group[x]-group[y])%3;
      fa[fy]=fx;
    }
  }
  cout<<ans<<endl;
  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值