1. 程式人生 > 其它 >2022/4/10 樹狀陣列專場遊記

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

  • 彷彿是道普通樹狀陣列,再加上一個小小的二分……
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;
}