Codeforces Round #702 (Div. 3)

E. Accidental Victory

main idea:二分查找,贪心;

A championship is held in Berland, in which n players participate. The player with the number i has ai (ai≥1) tokens.
The championship consists of n−1 games, which are played according to the following rules:
in each game, two random players with non-zero tokens are selected;
the player with more tokens is considered the winner of the game (in case of a tie, the winner is chosen randomly);
the winning player takes all of the loser’s tokens;
The last player with non-zero tokens is the winner of the championship.
All random decisions that are made during the championship are made equally probable and independently.
For example, if n=4, a=[1,2,4,3], then one of the options for the game (there could be other options) is:
during the first game, the first and fourth players were selected. The fourth player has more tokens, so he takes the first player’s tokens. Now a=[0,2,4,4];
during the second game, the fourth and third players were selected. They have the same number of tokens, but in a random way, the third player is the winner. Now a=[0,2,8,0];
during the third game, the second and third players were selected. The third player has more tokens, so he takes the second player’s tokens. Now a=[0,0,10,0];
the third player is declared the winner of the championship.
Championship winners will receive personalized prizes. Therefore, the judges want to know in advance which players have a chance of winning, i.e have a non-zero probability of winning the championship. You have been asked to find all such players.

Input
The first line contains one integer t (1≤t≤104) — the number of test cases. Then t test cases follow.
The first line of each test case consists of one positive integer n (1≤n≤2⋅105) — the number of players in the championship.
The second line of each test case contains n positive integers a1,a2,…,an (1≤ai≤109) — the number of tokens the players have.
It is guaranteed that the sum of n over all test cases does not exceed 2⋅105.

Output
For each test case, print the number of players who have a nonzero probability of winning the championship. On the next line print the numbers of these players in increasing order. Players are numbered starting from one in the order in which they appear in the input.

Example
input
2
4
1 2 4 3
5
1 1 1 1 1
output
3
2 3 4
5
1 2 3 4 5

题意:有n个运动员,每场比赛随机抽取两人,token数多的人获胜,,输的人的token全部给胜者,同时自己的token清零。最后拥有非零token的人为胜者。问有多少运动员有机会获胜。
我们容易看出如果暴力去判断每一个人的话,是两层循环o(n2)的复杂度,超时。我们想,是不是有些玩家不需要判断,例如,某玩家被判断有机会赢得冠军,那么token数大于等于此玩家的玩家,都有机会赢得冠军,因此,我们只需要找的有机会赢得冠军,并且token数最少的那位玩家即可。

Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6;
int t,a,b;
void solve() {
	int n;
	scanf("%d",&n);
	vector<pair<int,int> > per;//储存玩家的token数量和id
	for(int i=1; i<=n; i++) {
		int token;
		scanf("%d",&token);
		per.push_back(make_pair(token,i));
	}
	sort(per.begin(),per.end());//按first(token)排序
	int l=0,r=n-1,mid;
	while(l<r) {
		mid = (l+r)/2;
		ll k=0;
		for(int i=0; i<=mid; i++)
			k+=per[i].first;
		bool f=0;
		for(int i=mid+1; i<n; i++) {
			if(k<per[i].first) {
				f=1;
				break;
			}
			k +=per[i].first;
		}
		if(!f) r=mid; //key
		else l=mid+1;
	}
	vector<int> ans;//大于等于r的都有机会拿第一 
	for(int i=r; i<n; i++) {
		ans.push_back(per[i].second);
	}
	sort(ans.begin(),ans.end());
	cout<<ans.size();putchar('\n');
	for(int i=0; i<ans.size(); i++)
		printf("%d ",ans[i]);
	putchar('\n');

}
int main() {
	cin>>t;
	while(t--)
		solve();
	return 0;
}

D. Permutation Transformation

main idea:(笛卡尔树)
在这里插入图片描述

input
3
5
3 5 2 1 4
1
1
4
4 3 1 2
output
1 0 2 3 1
0
0 1 3 2

这题我看B站学到的,自己看的时候题都没看明白啥意思哈哈。本题和笛卡尔树有点关系。不太了解笛卡尔树的点这里 click here.然后我们有这个背景知识了以后,应该好理解这题的意思了。
题目大意给你一个permutation,然后依次找到这个排列里最大元素,第一次找的的本排列中最大元素的位置,然后此位置的深度复制为0,以后深度依次加一,然后以这个元素的位置为分界点,向左右两侧的区间里找最大元素,深度为1,然后继续迭代下去,一直进行到所有元素的位置被标记即结束。还不太理解的话,在看题目中给的样例模拟的过程就差不多了。

Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=110;
int n;
int a[maxn],ans[maxn];
void f(int l,int r,int d) {
	if(l>r) return;
	int max_pos = max_element(a + l,a + r + 1) - a;//最大元素在数组中的位置
	ans[max_pos] = d;
	f(l,max_pos-1,d+1);
	f(max_pos+1,r,d+1);
}
void solve() {
	int n;
	scanf("%d",&n);
	for(int i=1; i<=n; i++)
		scanf("%d",&a[i]);
	f(1,n,0);
	for(int i=1; i<=n; i++)
		printf("%d ",ans[i]);
	putchar('\n');
}
int main() {
	int t;
	cin>>t;
	while(t--) {
		solve();
	}
	return 0;
}

G. Old Floppy Drive

main idea:二分,数学

Polycarp was dismantling his attic and found an old floppy drive on it. A round disc was inserted into the drive with n integers written on it.
Polycarp wrote the numbers from the disk into the a array. It turned out that the drive works according to the following algorithm:
the drive takes one positive number x as input and puts a pointer to the first element of the a array;
after that, the drive starts rotating the disk, every second moving the pointer to the next element, counting the sum of all the elements that have been under the pointer. Since the disk is round, in the a array, the last element is again followed by the first one;
as soon as the sum is at least x, the drive will shut down.
Polycarp wants to learn more about the operation of the drive, but he has absolutely no free time. So he asked you m questions. To answer the i-th of them, you need to find how many seconds the drive will work if you give it xi as input. Please note that in some cases the drive can work infinitely.
For example, if n=3,m=3, a=[1,−3,4] and x=[1,5,2], then the answers to the questions are as follows:
the answer to the first query is 0 because the drive initially points to the first item and the initial sum is 1.
the answer to the second query is 6, the drive will spin the disk completely twice and the amount becomes 1+(−3)+4+1+(−3)+4+1=5.
the answer to the third query is 2, the amount is 1+(−3)+4=2.

Input
The first line contains one integer t (1≤t≤104) — the number of test cases. Then t test cases follow.
The first line of each test case consists of two positive integers n, m (1≤n,m≤2⋅105) — the number of numbers on the disk and the number of asked questions.
The second line of each test case contains n integers a1,a2,…,an (−109≤ai≤109).
The third line of each test case contains m positive integers x1,x2,…,xm (1≤x≤109).
It is guaranteed that the sums of n and m over all test cases do not exceed 2⋅105.
Output
Print m numbers on a separate line for each test case. The i-th number is:
−1 if the drive will run infinitely;
the number of seconds the drive will run, otherwise.

Example
inputCopy
3
3 3
1 -3 4
1 5 2
2 2
-2 0
1 2
2 2
0 1
1 2
outputCopy
0 6 2
-1 -1
1 3

题意:给你一个数组长度为n,然后有一个指针从第一个元素开始向后移动,数组的最后一个元素紧连着数组的第一个元素,依次循环下去,给你m次询问,每次询问一个数X,问你在何时第一次前面所有走过的元素总和大于等于X,如果存在输出此时的时间,否则输出-1;

我们找到一个d(每次循环结束后,对下次一循环所贡献的值)ma数组(非递减的前缀和),类似于数列的公差。然后通过d建立起两个循环之间的联系,将你需要查找的数X,缩小到第一个循环内,然后二分ma数组找到第一个大于等于转化后的 X '.
Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+10;
int n,m;
ll a[N],ma[N];
void solve() {
	int n,m;
	scanf("%d%d",&n,&m);
	ma[0]=0;//初始化 
	ll sum=0;//前缀和
	for(int i=1; i<=n; i++) {
		scanf("%lld",&a[i]);
		sum+=a[i];
		ma[i]=max(ma[i-1],sum);//保证有序 
	}
	ll d = sum;//公差
	while(m--) {
		ll ans=0;
		int x;
		scanf("%d",&x);
		if(ma[n]<x&&d<=0) {
			printf("-1 ");
			continue;
		}
		ll round;
		if(x<=ma[n]) round=0;
		else round=(x-ma[n]-1) / d + 1;
		x -= round*d;
		ans += round*n;
		int l=1,r=n;
		while(l<r) {//二分求第一个大于等于转换后x的位置
			int mid=(l+r)/2;
			if(ma[mid]>=x) r=mid;
			else l=mid+1;
		}
		ans+=l;
		printf("%lld ",ans-1);
	}
	putchar('\n');
}
int main() {
	int t;
	scanf("%d",&t);
	while(t--) {
		solve();
	}
	return 0;
}
 

以上思路学习于b站大佬,如有问题,小白尽力解答

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值