A - A
Pak Chanek 有一个包含 nnn 个正整数的数组aaa。由于他正在学习如何计算两个数字的向下取整平均值,他希望在他的数组 aaa 上进行练习。当数组 aaa 至少有两个元素时,Pak Chanek 将执行以下三步操作:
∙\bullet∙选择两个不同的索引 iii 和 j(1≤i,j≤∣a∣;i≠j)j (1≤i,j≤∣a∣; i≠j)j(1≤i,j≤∣a∣;i=j),注意 ∣a∣∣a∣∣a∣ 表示数组 aaa 的当前大小。
∙\bullet∙将 ⌊2ai+aj2⌋⌊\frac{2a_i +a_j}{2}⌋⌊22ai+aj⌋∗ 附加到数组的末尾。
∙\bullet∙从数组中移除元素 aia_iai 和 aja_jaj并连接数组的剩余部分。
例如,假设 a=[5,4,3,2,1,1]a=[5,4,3,2,1,1]a=[5,4,3,2,1,1] 。如果我们选择 i=1i=1i=1 和 j=5j=5j=5 ,则结果数组将是 a=[4,3,2,1,3]a=[4,3,2,1,3]a=[4,3,2,1,3] 。如果我们选择 i=4i=4i=4 和 j=3j=3j=3 ,则结果数组将是 a=[5,4,1,1,2]a=[5,4,1,1,2]a=[5,4,1,1,2]。
在所有操作完成后,数组将只包含一个元素 xxx。如果 Pak Chanek 进行最佳操作,找出 xxx 的最大可能值。
∗ ⌊x⌋⌊x⌋⌊x⌋ 表示 xxx 的向下取整函数,即小于或等于 xxx 的最大整数。例如,⌊6⌋=6、⌊2.5⌋=2、⌊−3.6⌋=−4⌊6⌋=6、⌊2.5⌋=2、⌊−3.6⌋=−4⌊6⌋=6、⌊2.5⌋=2、⌊−3.6⌋=−4 和 ⌊π⌋=3⌊π⌋=3⌊π⌋=3
找规律,可以通过打表发现,只要每次把两个最小的元素合并起来,就能使最终的答案最大化。
那怎么找到最小的两个值呢?怎么把合并的值放到末尾呢?怎么删除选中的两个值呢?这里我们可以偷个懒,因为要合并n−1n-1n−1次,我们就循坏n−1n-1n−1次,每次先把aaa数组排一遍升序,我们就锁定了两个最小值。
现在是最关键的时刻,虽然题目说每次会把合并之后的结果放在数组的末尾,但下一次循环我们还是会排序,所以先把它放在a[1]a[1]a[1],由于每次操作aaa数组的最后一项都会蹿到前面去,所以我们就先把它放在a[2]a[2]a[2]。
这样我们就完美的删除了选中的两个数,并保留了aaa数组的其余项和选中的两个数合并之后的结果。
#include<bits/stdc++.h>//献上代码
using namespace std;
int a[55],t,n; //数据水是我时间复杂度的证明,O(n log n)
int main(){cin>>t;while(t--){cin>>n;for(int i=1;i<=n;i++)cin>>a[i];for(int i=1;i<n;i++){sort(a+1,a+1+n-i+1); //注意,我们在a数组里删除了值a[1]=(a[1]+a[2])/2,a[2]=a[n];}cout<<a[1]<<endl;//最后a数组只剩1个值了,于是输出a[1]}return 0;
}
B - B
给定一个由互不相同的整数组成的数组aaa。每次操作中,你可以选择:
∙\bullet∙选取数组的某个非空前缀∗∗∗,并将其替换为该前缀的最小值;
∙\bullet∙选取数组的某个非空后缀†\dagger†,并将其替换为该后缀的最大值。
注意:你可以选择整个数组aaa作为操作对象。
对于每个元素aia_iai,判断是否存在一系列操作能将数组aaa转化为aia_iai——即最终数组aaa仅包含该元素aia_iai 。
请输出一个长度为nnn的二进制字符串,其中第iii个字符为111表示存在这样的操作序列,否则为000。
∗∗∗数组的前缀是指由前kkk个元素组成的子数组(kkk为任意整数)。
†\dagger†数组的后缀是指由后kkk个元素组成的子数组(kkk为任意整数)。
首先我们可以证明如果一个数是某个前缀的最小值,或是某个后缀的最大值,就一定可以保留下来。
因为如果这个数是某个前缀的最小值,就可以先进行一次前缀操作,后面不管是什么数就都能经过几次后缀操作压缩成一个数,最后再进行一次前缀或后缀操作就能保留该数。
例如,a=a=a={5,6,3,1,75,6,3,1,75,6,3,1,7},此时我们想要判断a3a_3a3能不能保留下来,就先判断它是不是某个前缀的最小值,结果是的,就先进行一次前缀操作,a=a=a={3,1,73,1,73,1,7},接着进行一次后缀操作,a=a=a={3,73,73,7},最后再进行一次前缀操作,a3a_3a3就保留了下来。
注意,最后一次操作要根据情况判断是进行前缀操作还是进行后缀操作。
C - C
Nene 发明了一种基于递增整数序列 a1,a2,…,aka_1,a_2 ,…,a_ka1,a2,…,ak 的新游戏。
在这个游戏中,最初有 nnn 名玩家排成一行。在每一轮游戏中,发生以下情况:
Nene 找到第 a1、a2、…a_1 、a _2 、…a1、a2、…和 aka_kak名玩家,他们会同时被踢出游戏。如果第 iii 名玩家应该被踢出,但排成一行的玩家少于 iii 名,则跳过该玩家。
一旦在某一轮中没有人被踢出游戏,所有仍在游戏中的玩家将被宣布为胜利者。
例如,考虑有a=[3,5]a=[3,5]a=[3,5] 和 n=5n=5n=5 名玩家的游戏。假设玩家按顺序被命名为玩家 A、玩家 B、
…、玩家 E。
那么,在第一轮之前,玩家排成 ABCDE。Nene 找到第 333 和第 555 名玩家。这些是玩家 C 和 E。他们在第一轮被踢出。现在玩家排成 ABD。
Nene 找到第 333 和第 555 名玩家。第 333 名玩家是玩家 D,而排成一行中没有第 555 名玩家。因此,只有玩家 D 在第二轮被踢出。
在第三轮中,没有人被踢出游戏,因此游戏在这一轮结束。玩家 A 和 B 被宣布为胜利者。
Nene 尚未决定最初有多少人会加入游戏。Nene 给了你 qqq 个整数 n1,n2,…,nqn_1 ,n_2 ,…,n_qn1,n2,…,nq ,你应该独立回答以下问题:
如果游戏最初有 nin_ini 名玩家,最终会有多少人被宣布为胜利者?
先找出nnn数组的最小值,然后对于每次询问只要输出(ai)+1(a_i)+1(ai)+1%mn+1mn+1mn+1。怎么证明?
如图,在333下标之后包括333下标的元素都被删除了,最后只剩下两个元素,如果还是不信邪的话,自己造几个样例试试吧!
D - D
给定两个长度为n的排列aaa和bbb。排列是指包含nnn个从111到nnn不重复元素的数组。例如数组[2,1,3][2,1,3][2,1,3]是排列,但[0,1][0,1][0,1]和[1,3,1][1,3,1][1,3,1]不是。
你可以(不限次数)选择两个下标iii和jjj,然后同时交换aia_iai 与aja_jaj 以及bib_ibi与bjb_jbj。
你需要最小化两个排列中的逆序对总数。排列p中的逆序对是指满足i<ji<ji<j且pi>pjp_i>p_jpi>pj的下标对(i,j)(i,j)(i,j)。例如当p=[3,1,4,2,5]p=[3,1,4,2,5]p=[3,1,4,2,5]时,共有333个逆序对(具体为(1,2)、(1,4)(1,2)、(1,4)(1,2)、(1,4)和(3,4)(3,4)(3,4))。
首先用一个结构体接入a,b数组,然后按a数组排序,最后输出就行了。
#include<bits/stdc++.h>
using namespace std;
struct tt{int a,b;
}c[200005];
bool cmp(tt x,tt y){return x.a<y.a;
}
int main(){int t;cin>>t;while(t--){int n;cin>>n;for(int i=1;i<=n;i++)cin>>c[i].a;for(int i=1;i<=n;i++)cin>>c[i].b;sort(c+1,c+1+n,cmp);for(int i=1;i<=n;i++)cout<<c[i].a<<" ";cout<<endl;for(int i=1;i<=n;i++)cout<<c[i].b<<" ";cout<<endl; }return 0;
}
E - E
你有一个数组 a1,a2,…,ana_1,a_2,…,a_na1,a2,…,an。回答 q 个以下形式的查询:
如果我们将数组的 al,al+1,…,ara_l,a_{l+1},…,a_ral,al+1,…,ar 范围内的所有元素改为kkk,整个数组的和会是奇数吗?
注意,查询是独立的,不会影响后续的查询。
前缀和数组来存储区间总和。注意1,因为我们只需要判断总和是不是奇数,所以我们可以%2。注意2,把一个区间的值都改成k等于把一个区间的值都加上k,当然,仅在本题生效。
为什么呢?当然是通过打表找规律发现的,想知道为什么的自己试几组数据吧!
#include<bits/stdc++.h>
using namespace std;
long long a[200005],s[200005];
int main(){int t;cin>>t;while(t--){memset(s,0,sizeof s);memset(a,0,sizeof a);long long n,q,cs=0;//cs数组总和cin>>n>>q;for(int i=1;i<=n;i++)cin>>a[i],cs+=a[i]%2,s[i]=a[i]%2+s[i-1];while(q--){long long l,r,k,css=cs;cin>>l>>r>>k;css+=(r-l+1)*k%2-(s[r]-s[l-1])%2;if(css%2==1){cout<<"YES"<<endl;}else{cout<<"NO"<<endl;}}}return 0;
}
F - F
体面过于复杂,链接F - F
这题很显然每次要合并最长的一条路径才是最优操作,然而每次进行这种操作都会删除2个叶子节点,所以先找出有多少个叶子节点,答案就是⌈叶子节点数⌉2\frac{\left \lceil 叶子节点数 \right \rceil}{2}2⌈叶子节点数⌉。