2022/4/10 树状数组专场游记

别问,问就是AK了就是游记

终于告别DP了

比赛链接

A.Stars

  • 由于出题人十分良心地手动帮我们排好了序,所以我们只需要在录入每一个点时用树状数组查询 \(x\le x_i\) 的点即可;
  • 数组中也只需要记录 \(x\);
  • 结果因为数组开得不够大和没考虑 \(x=0\) 的情况而吃了 \(4\) 发罚时……我愿称之为开门红
AC code
#include<iostream>
#include<algorithm>
#include<cmath>
#include<exception>
#include<cstring>
#include<ios>
using namespace std;

const int N=15010;
const int M=32010;

int n;
int c[M];
int ans[N];

void add(int x,int y){
	for(;x<M;x+=x&-x)
		c[x]+=y;
	return ;
}

int ask(int x){
	int cnt=0;
	for(;x;x-=x&-x)
		cnt+=c[x];
	return cnt;
}

int main(){
	scanf("%d",&n);
	int x,y;
	for(int i=1;i<=n;++i){
		scanf("%d%d",&x,&y);
		int cnt=ask(x+1);
		++ans[cnt];
		add(x+1,1);
	}
	for(int i=0;i<n;++i)
		printf("%d\n",ans[i]);
	return 0;
}

B.Inversions

  • 求逆序对的板题;
  • 仅有的两道一遍 A 的题目之一
板题就不用代码了吧?
#include<iostream>
#include<algorithm>
#include<cmath>
#include<exception>
#include<cstring>
#include<ios>
using namespace std;

const int N=65537+10;

int n;
int b[N],c[N];

struct memr{
	int a,i;
	bool operator<(const memr &x)const{
		return a<x.a;
	}
}num[N];

void add(int x,int y){
	for(;x<=n;x+=x&-x)
		c[x]+=y;
	return ;
}

int ask(int x){
	int cnt=0;
	for(;x;x-=x&-x)
		cnt+=c[x];
	return cnt;
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%d",&num[i].a);
		num[i].i=i;
	}
	sort(num+1,num+n+1);
	for(int i=1,tot=0;i<=n;++i){
		if(i-1 && num[i].a==num[i-1].a)
			b[num[i].i]=tot;
		else b[num[i].i]=++tot;
	}
	long long ans=0;
	for(int i=1;i<=n;++i){
		ans+=(i-ask(b[i])-1);
		add(b[i],1);
//		printf("%d\n",ans);
	}
	printf("%lld",ans);
	return 0;
}
  • 话说这题可以在洛谷上多倍经验(别问我怎么知道的;

C.Matrix

  • 算是个二维树状数组的板题;
  • 将树状数组的原始值设定为差分后的矩阵即可,\(0|1\) 的翻转通过奇偶性来实现;
AC code
#include<iostream>
#include<algorithm>
#include<cmath>
#include<exception>
#include<cstring>
#include<ios>
using namespace std;

inline int read(){
	int s=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		s=s*10+int(ch-'0');
		ch=getchar();
	}
	return s*f;
}

int T;
int n,q;
int c[1005][1005];

void add(int x,int y,int z){
	for(int i=x;i<=n;i+=i&-i)
		for(int j=y;j<=n;j+=j&-j)
			c[i][j]+=z;
	return ;
}

int ask(int x,int y){
	int cnt=0;
	for(int i=x;i;i-=i&-i)
		for(int j=y;j;j-=j&-j)
			cnt+=c[i][j];
	return cnt;
}

int main(){
	T=read();
	while(T--){
		memset(c,0,sizeof(c));
		n=read(),q=read();
		char opt;
		int x,y,a,b;
		while(q--){
			cin>>opt;
			x=read(),y=read();
			if(opt=='C'){
				a=read(),b=read();
				add(x,y,1);
				add(x,b+1,-1);
				add(a+1,y,-1);
				add(a+1,b+1,1);
			}
			else printf("%d\n",(ask(x,y)%2+2)%2);
		}
		puts("");
	}
	return 0;
}

D.MooFest

  • 我认为的本次最难的题目,没有之一;
  • 考虑到音量要取 \(Max\),将牛的坐标离散化之后按音量升序排列,这样计算时就不需要取 \(Max\);
  • 接下来求 \(dis(i,j)\),考虑到绝对值的性质,将树状数组分为两个并行的部分,一部分用来存 \(x\) 左边的牛坐标的和,另一部分用来存 \(x\) 右边的牛坐标的和;
  • 求出了这些还不行,再将每个部分划为两半,一半存坐标之和,另一半存牛的只数,这样计算时只需要用坐标之和与 牛的只数与当前坐标的乘积 作差即可;
  • 虽然但是,这就是唯二一遍 A 的题目的另一道……
AC code
#include<iostream>
#include<algorithm>
#include<cmath>
#include<exception>
#include<cstring>
#include<ios>
using namespace std;

inline int read(){
	int s=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		s=s*10+int(ch-'0');
		ch=getchar();
	}
	return s*f;
}

#define re register
#define id(i) cow[i].id

const int N=2e4+10;

int n;
int b[N],c[4][N];

struct memr{
	int x,v,id;
}cow[N];

bool ap(memr _,memr __){
	return _.x<__.x;
}

bool bp(memr _,memr __){
	return _.v<__.v;
}

void add(int m,int x,int y){
	for(;x<=n;x+=x&-x){
		c[m][x]+=y;
	}
	return ;
}

int ask(int m,int x){
	int cnt=0;
	for(;x;x-=x&-x){
		cnt+=c[m][x];
	}
	return cnt;
}

int main(){
	n=read();
	for(re int i=1;i<=n;++i){
		cow[i].v=read(),cow[i].x=read();
		cow[i].id=i;
	}
	sort(cow+1,cow+n+1,ap);
	int tot=0;
	for(int i=1;i<=n;++i){
		if(i-1 && cow[i].x==cow[i-1].x)
			b[cow[i].id]=tot;
		else b[cow[i].id]=++tot;
	}
	sort(cow+1,cow+n+1,bp);
	long long ans=0;
	for(re int i=1;i<=n;++i){
		ans+=1ll*cow[i].v*(1ll*cow[i].x*ask(2,b[id(i)]+1)-ask(0,b[id(i)]+1));
		ans+=1ll*cow[i].v*(ask(1,tot-b[id(i)]+1)-1ll*cow[i].x*ask(3,tot-b[id(i)]+1));
		add(0,b[id(i)]+1,cow[i].x);
		add(1,tot-b[id(i)]+1,cow[i].x);
		add(2,b[id(i)]+1,1);
		add(3,tot-b[id(i)]+1,1);
	}
	printf("%lld",ans);
	return 0;
}

E.KiKi's K-th Number

  • 仿佛是道普通树状数组,再加上一个小小的二分……
  • 白给是因为输出了"No Find!"……
AC code
#include<iostream>
#include<algorithm>
#include<cmath>
#include<exception>
#include<cstring>
#include<ios>
using namespace std;

inline int read(){
	int s=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		s=s*10+int(ch-'0');
		ch=getchar();
	}
	return s*f;
}

const int N=1e5+10;
int c[N],num[N];

int m;

void add(int x,int v){
	for(;x<N;x+=x&-x){
		c[x]+=v;
	}
	return ;
}

int ask(int x){
	int cnt=0;
	for(;x;x-=x&-x){
		cnt+=c[x];
	}
	return cnt;
}

void find(int v,int k){
	int ansv=ask(v);
	if(ask(N-10)-ansv<k){
		puts("Not Find!");
		return ;
	} 
	int l=v,r=1e5;
	while(l<r){
		int mid=(l+r)>>1;
		if(ask(mid)-ansv<k)
			l=mid+1;
		else r=mid;
	}
	printf("%d\n",l);
	return ;
}

int main(){
	while(scanf("%d",&m)!=EOF){
		memset(c,0,sizeof(c));
		memset(num,0,sizeof(num));
		int p,a,k;
		while(m--){
			p=read(),a=read();
			if(p==2){
				k=read();
				find(a,k);
			}
			else{
				if(p==0){
					add(a,1);
					num[a]++;
				}
				else{
					if(num[a])
						add(a,-1),num[a]--;
					else puts("No Elment!");
				}
			}
		}
	}
	return 0;
}
posted @ 2022-04-10 17:19  Star_LIcsAy  阅读(35)  评论(0)    收藏  举报