2022/4/10 樹狀陣列專場遊記
阿新 • • 發佈:2022-04-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;
}