中南oj 2019年1月月赛 Hello 2019! (Div1+Div2) 题解

A: Rikka和Galgame

不会待补

B: 假装是区间众数

思路:莫队算法,离线给所有区间排序,然后按顺序求答案即可,复杂度n^1.5

// res[ i ]=x 含义为有x个数出现了 i 次

#include<bits/stdc++.h>
#define db double
using namespace std;
const int maxn=1e5+10;
int a[maxn],b[maxn],vis[maxn],ans[maxn],block;
int res[maxn],mx;
struct node
{
	int l,r,id;
	bool operator<(const node&t)const
	{
		if(l/block==t.l/block)
		return r<t.r;
		return l/block<t.l/block;
	}
}c[maxn];
void up(int i,int v)
{
	res[vis[a[i]]]--;
	if(v==1)
	{
		vis[a[i]]++;
		res[vis[a[i]]]++;
		mx=max(mx,vis[a[i]]);
	}
	else
	{
		vis[a[i]]--;
		res[vis[a[i]]]++;
		if(res[mx]==0)mx--;	
	}
}
int main()
{
	int n,Q,l,r;
	scanf("%d%d",&n,&Q);
	block=int(sqrt(n));
	for(int i=1;i<=n;i++)
	scanf("%d",&a[i]),b[i]=a[i];
	sort(b+1,b+1+n);
	
	for(int i=1;i<=n;i++)
	a[i]=lower_bound(b+1,b+1+n,a[i])-b;
	
	for(int i=1;i<=Q;i++)
	scanf("%d%d",&c[i].l,&c[i].r),c[i].id=i;
	sort(c+1,c+1+Q);
	
	int p=c[1].l,q=c[1].r;
	for(int i=p;i<=q;i++)
	up(i,1);
	ans[c[1].id]=mx;
	for(int i=2;i<=Q;i++)
	{
		for(int j=p;j<c[i].l;j++)
		up(j,-1);
		for(int j=p-1;j>=c[i].l;j--)
		up(j,1);
		for(int j=q+1;j<=c[i].r;j++)
		up(j,1);
		for(int j=q;j>c[i].r;j--)
		up(j,-1);
		ans[c[i].id]=mx;
		p=c[i].l,q=c[i].r;
	}
	for(int i=1;i<=Q;i++)
	printf("%d\n",ans[i]);
}

C: 上杉绘梨衣的告别(一)——时光南站

思路:暴力枚举len即可(每0.5枚举一次),然后用二分查找出最近的算答案即可,复杂度len*m*log(n)

#include<bits/stdc++.h>
#define db double
using namespace std;
const int maxn=1e5+10;
int p[305],d[305],len,m,n;
db ans,pos,b[305];
void ss(db xx)
{
	for(int i=1;i<=n;i++)
	b[i]=d[i]+xx;
	db res=0;
	for(int i=1;i<=m;i++)
	{
		int x=lower_bound(b+1,b+1+n,p[i])-b;
		if(x>n)x--;
		else if(x==0)x++;
		db tmp=fabs(b[x]-p[i]);
		if(b[x]>p[i])
		{
			if(x!=1)
			tmp=min(tmp,fabs(b[x-1]-p[i]));
		}
		else if(b[x]<p[i])
		{
			if(x!=n)
			tmp=min(tmp,fabs(b[x+1]-p[i]));
		}
		res+=tmp;
	}
	if(res>ans)ans=res,pos=xx;
}
int main()
{
	while(cin>>len)
	{
		cin>>m;
		for(int i=1;i<=m;i++)
		cin>>p[i];
		cin>>n;
		for(int i=2;i<=n;i++)
		cin>>d[i];
		ans=0,pos=0;
		for(int i=0;i<=len-d[n];i++)
		{
			ss(i);
			if(i!=len-d[n])
			ss(0.5+i);
		}
		printf("%.3lf %.3f\n",pos,ans);
	}
}

D: 上杉绘梨衣的告别(二)——项链

不会待补

E: 喜闻乐见的逆序对

思路:重点讲这题(其实很水的),举个例子,五个数:1 2 3 4 5,贡献分别为 3 2 0 0 0,我先从大到小枚举这5个数,5的贡献是0,从空集插入一个5,4的贡献也为0,那么4就插到集合第0个数的后面,集合有两个数 4->5,3同理,插入后 3->4->5,接下来2的贡献为2,那么显然把2插入集合第二个数的后面,即3->4->2->5,1就插入到第三个数的后面,所以答案是 3 4 2 1 5。

ok,思路是不是很清晰了?不过还是没有完全解决这个问题,普通插入n*n的复杂度肯定不行,动态插入怎么搞?这时就要献出treap了(不会可以先学,顺便a了这个题,这题用到的treap十分简单),完美的用n*logn复杂度解决掉动态插入问题。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
struct node
{
	int num,v;
	bool operator<(const node&t)const
	{
		return num>t.num;
	}
}a[maxn];
int cnt,ans[maxn];
struct Node *null;
struct Node
{
	Node *ch[2];
	int r;
	int v;
	int s;
	Node(int v):v(v){ch[0]=ch[1]=null;r=rand();s=1;}
	void maintain()
	{
		s=1;
		s+=ch[0]->s;
		s+=ch[1]->s;
	}
};
void rotate(Node* &o,int d)
{
	Node *k=o->ch[d^1];o->ch[d^1]=k->ch[d];
	k->ch[d]=o;o->maintain();k->maintain();
	o=k;
}
void insert(Node* &o,int x,int k)
{
	if(o==null) o=new Node(x);
	else
	{
		int d=(k<=(o->ch[0]->s)?0:1);
		if(d==1)k=k-o->ch[0]->s-1;
		insert(o->ch[d],x,k);
		if(o->ch[d]->r>o->r)rotate(o,d^1);
	}
	o->maintain();
}

void dfs(Node* o)
{
	if(o==null)return;
	dfs(o->ch[0]);
	ans[++cnt]=o->v;
	dfs(o->ch[1]);
}
int main()
{
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	scanf("%d",&a[i].num);
	for(int i=1;i<=n;i++)
	scanf("%d",&a[i].v);
	sort(a+1,a+1+n);
	
	null=new Node(0);
	null->s=0;
	Node *root=null;
	
	for(int i=1;i<=n;i++)
	insert(root,a[i].num,a[i].v);
	
	dfs(root);
	for(int i=1;i<cnt;i++)printf("%d ",ans[i]);
	printf("%d\n",ans[cnt]);
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长沙橘子猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值