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;
}

浙公网安备 33010602011771号