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;
}
posted @ 2022-05-01 11:11  Star_LIcsAy  阅读(30)  评论(0)    收藏  举报