2022/5/1 五一集训#2游记
比赛链接:此处
\(\color{CornflowerBlue}{话说两道题也能叫AK吗★}\)
A.Disharmony Trees
- 和上次的 D.MooFest 有着异曲同工之妙
不能说毫不相似,只能说一模一样; - 解法:考虑到绝对值的性质,开四个树状数组,一个用于存坐标在 \(x\) 左边的树的个数,一个用于存坐标在 \(x\) 左边的树的坐标和,一个用于存坐标在 \(x\) 右边的树的个数,一个用于存坐标在 \(x\) 右边的树的坐标和;
- 按照高度从高到低排序后,每次计算完再将当前的树计入树状数组中去;
AC code
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#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 ll long long
#define ih(i) id[1][c[i].i]
#define ix(i) id[0][c[i].i]
const int N=1e5+10;
int t,n;
int id[2][N];
int a[5][N];//先左后右
struct memr{
int h,x,i;
bool operator<(const memr &m){
return h>m.h;
}
}c[N];
struct sortt{
int x,i;
bool operator<(const sortt &m)const{
return x<m.x;
}
}b[N],d[N];
void add(int m,int x,int v){
for(;x<=n+10;x+=x&-x){
a[m][x]+=v;
}
return ;
}
ll ask(int m,int x){
ll cnt=0;
for(;x;x-=x&-x)
cnt+=a[m][x];
return cnt;
}
int main(){
// t=read();
while(scanf("%d",&n)!=EOF){
memset(a,0,sizeof(a));
for(int i=1;i<=n;++i){
c[i].x=read(),c[i].h=read();
b[i].x=c[i].x,d[i].x=c[i].h;
b[i].i=d[i].i=c[i].i=i;
}
sort(b+1,b+n+1);
sort(d+1,d+n+1);
for(int i=1;i<=n;++i){
if(i-1 && b[i].x==b[i-1].x)//x
id[0][b[i].i]=id[0][b[i-1].i];
else id[0][b[i].i]=i;
if(i-1 && d[i].x==d[i-1].x)//h
id[1][d[i].i]=id[1][d[i-1].i];
else id[1][d[i].i]=i;
// cout<<id[1][d[i].i]<<endl;
}
// for(int i=1;i<=n;++i){
// printf("%d %d\n",ix(i),ih(i));
// }
sort(c+1,c+n+1);
ll ans=0;
for(int i=1;i<=n;++i){
ans+=1ll*ih(i)*(ask(0,ix(i)+1)*ix(i)-ask(1,ix(i)+1));
// printf("#%d %d %d\n",c[i].i,ix(i),ih(i));
// printf("%lld %lld\n",ask(0,ix(i)+1),ask(1,ix(i)+1));
ans+=1ll*ih(i)*(ask(3,n-ix(i)+1)-ask(2,n-ix(i)+1)*ix(i));
// printf("%lld %lld\n",ask(2,n-ix(i)+1),ask(3,n-ix(i)+1));
add(0,ix(i)+1,1);
add(1,ix(i)+1,ix(i));
add(2,n-ix(i)+1,1);
add(3,n-ix(i)+1,ix(i));
}
printf("%lld\n",ans);
}
return 0;
}
B. Little Artem and Dance
- 万万没想到啊,居然是道模拟
所以我为什么思考了五分钟线段树; - 观察样例(或者以及暴力程序)可以发现,虽然奇数与偶数之间的关系不断变化,但从环的角度看,奇数与偶数内部的关系却不会发生改变,所以我们考虑寻找通项公式;
- 只需要记录譬如 \(1\) 和 \(2\) 的位置,再通过这个结果推出其他数字即可;
- 我的奇怪写法:记录 \(f\) 为 \(1\) 的位置,\(s[0]\)、\(s[1]\) 分别表示偶数与奇数相对与不大于它的那个奇数的位移;
操作 \(1\) 时直接计算,当遇到操作 \(2\) 时:- 如果 \(f\) 在奇数位上,则意味着需要将 \(f\) 代表的奇数整体往后一位,偶数相对则往前一位;
- 如果 \(f\) 在偶数位上,则说明 \(f\) 代表的奇数整体需要往前一位,偶数往后一位;
- 所有计算时最好 \(\mod n\);
AC code
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#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=1e6+10;
int n,q;
int f;
int s[2];
int et;
int ans[N];
int g(int x){
if(x%2==1) return x;
return x-1;
}
int main(){
// freopen("r.txt","r",stdin);
// freopen("out.txt","w",stdout);
n=read(),q=read();
et=0;
s[0]=1;
f=1;
int opt,x;
while(q--){
opt=read();
if(opt==1){
x=read();
while(x<0) x+=n;
(et+=x)%=n;
(f+=x)%=n;
}
else{
if(f%2==1){
s[0]--,s[1]++;
f=0;
}
else{
s[0]++,s[1]--;
f=1;
}
}
}
for(int i=1;i<=n;++i){
ans[(g(i)+et+(s[i%2])+n)%n]=i;
}
for(int i=1;i<=n;++i){
printf("%d ",ans[i%n]);
}
return 0;
}

浙公网安备 33010602011771号