2022 8.23

唉唉,每题都写一个太累了,写成一个吧

由于我太逊了(真的,要不是我比有些人多学一会。。。)有的题看了题解也不会改,欣赏题面吧

T1

神仙题,求序列众数,但序列超级大(1e6,而且数的范围有2e9大小)

输入如果不优化都超时,std跑了4s(有个人O(nlogn)的解加了快读既然比std跑得快,大雾)

其实不需要用桶存储,先让我们看看题解:

因为题目中说了众数一定存在,所以我们假设我们当前枚举的这个数就是众数,既然这个数是众数,那么他出现的次数一定比其他所有数都多。

对于第一个数,那么在这之前它本身就是众数,如果下一个数和他相等,那么次数+,如果不等,次数-1,如果次数<0,说明众数肯定在后面还会有,那么我们就把当前这个数当做众数,继续去做,这样只要有众数,这样做一定是正确的。

P.S.众数:出现最多次数的数,在此题中是出现次数大于总数一半的数,且保证众数是唯一的

理解理解?

其实最难理解的就是为什么不把当前输入的数存起来,难道这样不会忽略掉一些数吗?

让我们举个栗子(我不会数学证明)
1.例如某一个数是众数,而他后面跟了很多非众数(都不相同)
	e.g:5 5 5 5 5 5 1 2 3 4 ...
 a)若后面的其他数数量不超过众数的出现次数,则对答案不产生影响
 b)若超过了众数出现次数,根据这题的定义,则这个序列不合法

2.例如众数是分散出现的
	e.g:5 3 5 3 5 3 5 3 2 3 5 5 5 3...
 把数组按照每一个众数分段,把上面1的结论推广到下面,可以说明:
 要么众数能完全覆盖其他数,要么序列不合法

  总的来说,由本题中众数要求出现n/2次以上,可推知:
1.即使把其他所有数全部连在一起也不可能盖过众数;
2.即使把别的数分开逐一抵消众数,也至少有一个块无法盖过众数。
  故此解法成立。证明完毕。(好像我写了一大堆废话?包括这句。。)

AC Code

#include<bits/stdc++.h>
using namespace std;
int t;
int main(){
	cin>>t;
	while(t--){
		int n;cin>>n;
		int cnt=-1,cs=-90;
		for(int i=1;i<=n;i++){
			int tmp;cin>>tmp;
			if(cnt<0) cs=tmp,cnt=0;
			if(tmp==cs) cnt++;
			else cnt--;
		}
		cout<<cs<<endl;
	}
}

T2

先上代码。带上代码看题目也许会简单一点。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n;
struct person{
	int t,w,id;
	bool ok;
	friend bool operator <(person a,person b){
		return (a.w-a.t)>(b.w-b.t);
	}
}s[N]; 
bool cmp(person a,person b){
	return (a.t!=b.t?a.t>b.t:a.id<b.id);
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>s[i].t>>s[i].w;
		s[i].id=i;s[i].ok=1;
	}
	int ans=0x3f3f3f3f,rnk=0,pos=1,npos=1;
	priority_queue<person> a;
	sort(s+1,s+1+n,cmp);
	while(s[pos].id!=1){
		a.push(s[pos]);
		pos++;rnk++;
	}
	rnk++;npos=pos+1;ans=rnk;
	while(!a.empty()){
		ans=min(ans,rnk);
//		printf("ANS %d\n",ans);
		person ln=a.top();a.pop();
		if(ln.w-ln.t+1>s[pos].t) break;
//		printf("Chose %d,%d,%d\n",ln.id,ln.t,ln.w);
		rnk--;
		s[pos].t-=(ln.w-ln.t+1);
		for(;npos<=n;npos++){
//			printf("What(%d)->ID[%d][L%d][R%d](%d)\n",npos,s[npos].id,s[npos].t,s[npos].w,a.size());
			if(s[npos].t>s[pos].t){
				a.push(s[npos]);
			}else break;
		}
		rnk=a.size()+1;
	}
	cout<<ans;
}

题目描述

【问题描述】
我们现在举行了一场搬砖比赛,其中第i 个人能搬的重量上限是w[i],
如果第i 人搬的砖的重量超过了w[i]的话,那么这个人会因为搬不动了而放
弃比赛,每一个人初始时搬的砖的重量为t[i]。保证初始的t[i]<=w[i],也就
是初始每一个人都不会因为搬不动而退赛。
其中一个人的排名是:比他搬的砖重的人数加一。
1 号选手是氪金玩家,我们希望1 号选手的排名尽量靠前,一号选手通
过充钱获得了一个技能,就是可以把自己搬的砖分给不同的人,但是他自
己搬的砖就会减少,通过让别人搬不动而退赛使得自己的排名上升,注意
别的选手是不能有这个操作的。
【输入格式】
第一行一个整数n
接下来n 行,每行两个整数t[i],w[i],分别表示第i 号选手初始搬砖重
量和重量上限
【输出格式】
输出一行,一个数,表示1 号选手能取得的最高成绩。
【输入样例18
20 1000
32 37
40 1000
45 50
16 16
16 16
14 1000
2 1000
【输出样例13
【输入样例27
4 4
4 4
4 4
4 4
4 4
4 4
5 5
【输出样例22
【数据范围】
对于30%的数据,n<=10;
对于50%的数据,n<=50;
对于100%的数据,1<=n<=10^5,0<=t[i],w[i]<=10^9

怎么样,标准贪心是吧。先上官方题解。

一看数据范围就是 nlong 的算法,先回忆下 nlong 的算法有哪些,无外乎就哪几种数据结构,还有一些分治类算法。

首先思考?怎么才能让排名上升?把砖给其他人然后让其他人爆掉,给谁呢?肯定是排名在他之前的人,具体给谁呢?反正都最多上升一名,给的砖越少越好,所以给wi-ti 最小的人。

关键问题是:给了别人砖,他的排名可能会掉,因为他的砖的数量减少了。那么直接贪心对不对呢?
如果可以让后面爆掉并且排名上升 1 就给,否则就不给。

错误的,因为就算当时排名下降以后还是可以升回来的,很容易举反例。所以我们还可以继续尝试。那么我们就要把初始时排名在他后面,但是他给了别人砖之后排名在他前面的人也拿过来,再从中选择一个 wi-ti 最小的同学。

什么时候结束呢?当他无论怎么给别人砖排名都没法上升的时候就结束了。所以怎么维护我们每次给砖的人呢?

讲解亿下

总的来说,搞一个比自己弱的人在目前来说是一点用都没有还浪费砖,但这不意味这我们以后不会搞他。现在只有搞比自己高的人才能获利,但是不论搞那个高的人都只会带来至多1的收益,甚至还会因为给出了砖而排名下降。

但是我们分析一下,在此时,不论是获利还是落后,我们都希望尽量给出的砖少,因此我们可以取当前比自己大的人中上限于当前最小的一个(落后是无法改变的,因为如果最小的花费都会导致落后,那更大的就更落后)

所以我们想到可以使用堆来维护:

1.push所有比他大的

2.检查堆顶是否可以搞,无论如何,弹出堆头(为了可以结束)

3.检查是否有新的比自己当前大的人,如有,入队

4.更新当前答案为堆的大小+1(记得加一)

5.若堆为空,结束

===end of solution===

其实今天有4道题,但我逊爆了不会做,唉。。。

如果文章有任何谬误,欢迎指出。

upd:2022/8/23 Tue 21:30 hzx:贺题去啦

感言

我太逊了

EOF

posted @ 2022-08-23 19:51  haozexu  阅读(27)  评论(0)    收藏  举报  来源