Codeforces Round 963 (Div. 2)
有一场考试一共 4 ∗ n 4*n 4∗n 道题目,其中答案为A
,B
,C
,D
的题目各 n n n 道, 现在你有一份考试的结果,由字母A
,B
,C
,D
组成,请问最多得到多少分。
每种选项最多答对n道题目,我们统计每个选项的出现次数 ,和n取较小值 ,将结果加起来即可。
#include
using namespace std;
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define rep(i,l,r) for(int i = l;i<=r;i++)
#define per(i,r,l) for(int i = r;i>=l;i--)
const int INF = 0x3f3f3f3f3f3f3f3f;
typedef pair<int,int> PII;
void solve(){
int n;
cin>>n;
string str;
cin>>str;
int cnt[150] = {0};
for(auto & p : str){
cnt[p] ++;
}
int ans = 0;
for(int i = 'A';i<='D';i++){
ans += min(cnt[i],n);
}
cout<<ans<<'\n';
}
signed main(){
int T = 1;
cin>>T;
while(T--){
solve();
}
return 0;
}
给你一个由n个正整数组成数组a , 你每次操作可以选择两个下标 ( i , j ) (i,j) (i,j) , 满足 a i a_i ai 与 a j a_j aj 的奇偶性不同。将 a i a_i ai 和 a j a_j aj 中的较小值转换为 a i + a j a_i+a_j ai+aj 。
问最少需要操作几次才能够使得数组a中的所有的数奇偶性相同。
我们将奇数和偶数分开来, 奇数在数组odd中,偶数在数组even中 , 那么我们每次操作即为从odd和even中各选择一个数, 最后目标为使odd数组为空或者even数组为空。
首先,如果odd数组初始为空,或者even初始数组为空,那么答案即为0 。
因为我们每次操作一定选择的是一个奇数和一个偶数,那么我们最终加和后的结果一定是奇数 ,于是每次操作只会使得奇数越来越多,偶数越来越少。
下面给出结论: 假设起初有 c n t cnt cnt个偶数 ,那么需要的操作次数最少为 c n t cnt cnt, 最多为 c n t + 1 cnt+1 cnt+1 。
我们假设选择的奇数为 x x x,偶数为 y y y。 如果 x < y x < y x<y 那么会让奇数 x x x 变为另一个奇数 x + y x+y x+y ; 如果 x > y x>y x>y 那么会让偶数 y y y变为奇数 x + y x+y x+y 。即只有第二种操作才能令偶数的数量减少一个。 因此最少需要cnt次才能将所有的偶数变为奇数。
因此我们应该尽可能地保证每次选择的奇数比偶数要大,于是我们每次一定会选择目前最大的奇数 m x mx mx , 如果在这种选择下仍然会出现奇数更小的情况,那么我们就一定无法在cnt次操作下完成目标了。 此时我们可以选择将mx和最大的偶数进行操作,得到的新的奇数一定比所有的偶数大,之后每次都选择这个奇数,便可以保证每次都满足奇数大于偶数
了。 最终需要cnt+1次操作。
#include
using namespace std;
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define rep(i,l,r) for(int i = l;i<=r;i++)
#define per(i,r,l) for(int i = r;i>=l;i--)
const int INF = 0x3f3f3f3f3f3f3f3f;
typedef pair<int,int> PII;
void solve(){
int n;
cin>>n;
vector<int> odd,even;
for(int i =1;i<=n;i++){
int x;cin>>x;
if(x%2) odd.push_back(x);
else even.push_back(x);
}
if((!odd.size()&&even.size())) {cout<<"0"<<endl;return ;}
if((odd.size()&&!even.size())) {cout<<"0"<<endl;return ;}
sort(odd.begin(),odd.end());
sort(even.begin(),even.end());
int sz = even.size();
int mx = odd.back();
queue<int> q;
for(auto p:even) q.push(p);
int ans = 0;
while(q.size()){
if(mx > q.front()) {
ans++;
mx = mx + q.front();
q.pop();
}else{
ans ++;
mx = mx + q.front();
}
}
cout<<min(ans,sz+1)<<'\n';
}
signed main(){
int T = 1;
cin>>T;
while(T--){
solve();
}
return 0;
}
有n个房间,每个房间都有一盏灯,起初所有的灯都是灭的,第 i i i个房间会在 a i , a i + k , a i + 2 ∗ k , . . . a_i, a_i+k,a_i+2*k,... ai,ai+k,ai+2∗k,... 的时间刻转换灯的状态,即在 [ a i , a i + k − 1 ] , [ a i + 2 ∗ k , a i + 3 ∗ k − 1 ] , . . . [a_i,a_i+k-1] , [a_i+2*k,a_i+3*k-1],... [ai,ai+k−1],[ai+2∗k,ai+3∗k−1],... 区间内亮灯。
问最早在哪个时刻,能保证所有n盏灯都是亮着的。如果不存在某个时刻所有的灯都是亮着的,那么输出-1
.
题意即为将所有房间的亮灯区间取交集,然后取最小值。
我们可以对所有的房间按照 a i a_i ai 从小到大进行排序 ,然后依次取交集, 因为每个区间都为 2 ∗ k 2*k 2∗k重复区间,所以每次取交集后一定也为 2 ∗ k 2*k 2∗k 重复区间,于是我们只需要使用 ( l , r ) (l,r) (l,r) 来表示区间 [ l + j ∗ 2 k , r + j ∗ 2 k ] [l+j*2k,r+j*2k] [l+j∗2k,r+j∗2k] 即可。
起初 l = a [ 1 ] , r = a [ 1 ] + k − 1 l = a[1], r = a[1]+k-1 l=a[1],r=a[1]+k−1 ,之后我们遍历每个区间 [ l i , r i ] [l_i,r_i] [li,ri] , 如果当前遍历的区间与交集的初始区间 [ l , r ] [l,r] [l,r] 相差距离超过 2 ∗ k 2*k 2∗k 便可以通过令 l , r l,r l,r 都加上 t t t个 2 ∗ k 2*k 2∗k(其中 t = l i − l 2 ∗ k t = \frac{l_i - l}{2*k} t=2∗kli−l ),来使得两个区间的左端点的距离不超过 2 ∗ k 2*k 2∗k , 此时如果 l i > r l_i > r li>r 那么取交集一定为空,我们尝试让 [ l , r ] [l,r] [l,r] 再向后移动一个 2 ∗ k 2*k 2∗k ,试图令 [ l i , r i ] [l_i,r_i ] [li,ri] 与 [ l + 2 k , r + 2 k ] [l+2k,r+2k] [l+2k,r+2k]取交集。 (取交集即为 l = m a x ( l , l i ) , r = m i n ( r , r i ) l= max(l,l_i), r = min(r,r_i) l=max(l,li),r=min(r,ri) )
如果某次遍历时使得 l > r l>r l>r 那么此时交集已经为空,直接输出-1即可。
如果遍历结束,那么最终的答案即为 l l l 。
#include
using namespace std;
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define rep(i,l,r) for(int i = l;i<=r;i++)
#define per(i,r,l) for(int i = r;i>=l;i--)
const int INF = 0x3f3f3f3f3f3f3f3f;
typedef pair<int,int> PII;
void solve(){
int n,k;
cin>>n>>k;
vector<int>a(n+5);
rep(i,1,n) cin>>a[i];
sort(a.begin()+1,a.begin()+1+n);
int l = a[1];
int r = a[1] + k - 1;
for(int i=2;i<=n;i++){
if(a[i] <= r && a[i] >= l){
l = a[i];
}
int t = (a[i] - l) / (2*k);
l += t * 2 * k;
r += t * 2 * k;
if(a[i] > r){
l+=2*k;
r+=2*k;
}
l = max(l,a[i]);
r = min(r,a[i]+k-1);
if(l > r) {
cout<<"-1\n";
return ;
}
}
cout<<l<<'\n';
}
signed main(){
int T = 1;
cin>>T;
while(T--){
solve();
}
return 0;
}
有一个长度为n的数组 a 0 , a 1 , . . . , a n − 1 a_0,a_1,...,a_{n-1} a0,a1,...,an−1 (注意本题的数组下标从0开始) , 以及一个整数 k k k , 我们每次选择一个长度为k的子数组,将其从数组a中删除,直到数组的中的数组元素数量不大于k。
问最终数组a的中位数最大是多少。本题的中位数为对数组的元素排序后,第 s z + 1 2 \frac{sz+1}{2} 2sz+1 个元素,其中 s z sz sz为数组的长度。
我们可以二分答案来求最终的中位数 。于是问题变成了: 给定一个数mid ,能否存在一个最终数组 a ′ a' a′ ,使得 a ′ a' a′ 中的大于等于mid的数的数量,比小于mid的数的数量更多。
我们将满足 a i ≥ m i d a_i \ge mid ai≥mid 的元素设置为 b i = 1 b_i = 1 bi=1 , 否则就设置为 b i = − 1 b_i = -1 bi=−1 。 我们尽可能让最终选出的 b ′ b' b′ 数组的和尽可能大,最后判断是否大于0即可。
但是我们不能任意选择剩下的元素,最终一定会剩下m个元素( m = ( n − 1 ) % k + 1 m =(n-1)\%k+1 m=(n−1)%k+1 ) ,并且最终剩下的数组 a i ′ ∈ { a i % k + t ∗ k } a'_i \in \{a_{i\%k + t * k}\} ai′∈{ai%k+t∗k} 即选出的最终的下标为 i i i的数的原始下标一定在 i i i的同余集中。
很好证明这点: 假设我们选出了第3个数(即下标为2),那么之前一定进行过选择了2个数,然后又删除了几组数(每组一定为k个数),所以这个数的原始下标一定为 t ∗ k + i t*k + i t∗k+i 。
我们用 d p [ i ] dp[i] dp[i] 表示考虑前 i i i个数, b [ i ] b[i] b[i]数组最大为多少,由于我们每次只能成组地删除元素,那么就代表了如果我们选择不要 b [ i ] b[i] b[i] 这个元素,那么 d p [ i ] = m a x ( d p [ i ] , d p [ i − k ] ) dp[i]=max(dp[i],dp[i-k]) dp[i]=max(dp[i],dp[i−k]) , 而如果我们选择要这个元素,那么 d p [ i ] = m a x ( d p [ i ] , d p [ i − 1 ] + b [ i ] ) dp[i] = max(dp[i],dp[i-1]+b[i]) dp[i]=max(dp[i],dp[i−1]+b[i]) , 最终比较 d p [ n − 1 ] > 0 dp[n-1]>0 dp[n−1]>0 是否成立即可。
#include
using namespace std;
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define rep(i,l,r) for(int i = l;i<=r;i++)
#define per(i,r,l) for(int i = r;i>=l;i--)
const int INF = 0x3f3f3f3f3f3f3f3f;
typedef pair<int,int> PII;
// 本题数据在数组中从下标0开始存储
int a[500005];
int b[500005];
int dp[500005];
int n,k;
bool check(int mid){
for(int i = 0;i < n;i++){
if(a[i] >= mid) b[i] = 1;
else b[i] = -1;
}
dp[0] = b[0];
for(int i = 1;i<n;i++){
if(i%k == 0){
dp[i] = max(dp[i-k],b[i]);
}else{
dp[i] = dp[i-1] + b[i];
if(i > k)
dp[i] = max(dp[i],dp[i-k]);
}
}
return dp[n-1] > 0;
}
void solve(){
cin>>n>>k;
rep(i,0,n-1) cin>>a[i];
int l,r;
l = 1;r = 1e9;
while(l<r){
int mid = (l+r+1)>>1;
if(check(mid)){
l = mid;
}else{
r = mid-1;
}
}
cout<<l<<'\n';
}
signed main(){
int T = 1;
cin>>T;
while(T--){
solve();
}
return 0;
}