C. Light Switches
题意:
房间的灯最初均为关闭状态,安装芯片后,它会每隔k分钟改变一次房间的灯光状态,即会打开灯光k分钟,关闭k分钟再打开,依次重复。
有n个房间,在不同时间安装芯片,最早什么时间可以使这些灯都在打开状态,如果不存在所有房间灯都打开的时刻·,则输出-1.
解题思路:
1.将所有时刻都排序,取最大时刻的亮灯区间
2.依次与数组中别的时刻的亮灯区间取交集
3.取交集时,我们应先将此时刻变换到接近区间的值,然后利用交集更新l,r
4.如果最终交际存在即l<r,则输出最大时刻,否则输出-1
解题代码:
#include <bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl '\n'
using namespace std;
void solve()
{
int n,k;
cin>>n>>k;
int a[n];
for(int i=0;i<n;i++)
{
cin>>a[i];
}
sort(a,a+n);
int l=a[n-1],r=a[n-1]+k-1;
for(int i=0;i<n-1;i++)
{
a[i]=((l-a[i])/(2*k))*(2*k)+a[i];
if(a[i]+k<l)
a[i]+=2*k;
l=max(l,a[i]);
r=min(r,a[i]+k-1);
}
if(l>r)
{
cout<<"-1\n";
return;
}
cout<<l<<endl;
}
signed main() {
IOS
int T = 1;
cin >> T;
while (T--)
{
solve();
}
return 0;
}
D. Med-imize
题意:
给出两个整数n,k以及一个整数数组a,你可以进行操作:任选l,r,但需保证r-l+1=k,可以将下标l-r的元素删除,当a的长度大于k时,必须进行此操作,进行操作后,求出数组a剩余元素的最大可能中值t,(长度为 n 的数组的中值是按非递减顺序对元素进行排序后索引为 ⌊(n+1)/2⌋ 的元素)
解题思路:
这个题是二分+dp
1.由于a的长度大于k时,必须进行操作,因此我们的最终序列长度为m=(n-1)%k+1
2.二分我们需要确定l,r的值,由于mid值这个值在数组a的区间内,因此,我们可以取l为最小值,r为最大值
3.然后就是普通的二分模板
4.dp出现在二分的check函数里,由于最终长度为m,(m=(n-1)%k+1),因此
所有下标在最后的保留的序列中应为j=(i-1)%k+1;
5.我们用数组f记录每次选择的比中值大的数的最大个数
f[j]=max(f[j],f[j-1]+p);//p为记录a[i]是否大于此时选取的中值,如果大于取1,否则取0
6.接下来我们需要看比最大值大的个数有多少个,如果f[m]>=m-(m+1)/2+1,则证明mid值可以取得更大,因此我们可以将l取为中值,否则就证明mid值取得较大,将r取mid-1
7.最终将l=r,输出即可
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t,n,k,m;
int a[500010];
int f[500010];
bool check(int mid)
{
for(int i=0;i<=m;i++)
{
f[i]=0;
}
for(int i=1;i<=n;i++)
{
int j=(i-1)%k+1;
int p=0;//p为记录a[i]是否大于此时选取的中值
if(a[i]>=mid)
p=1;
if(j==1)
f[j]=max(f[j],p);
else
f[j]=max(f[j],f[j-1]+p);
}
return (f[m]>=m-(m+1)/2+1);
}
void solve()
{
cin>>n>>k;
int l=INT_MAX,r=-1;
for(int i=1;i<=n;i++)
{
cin>>a[i];
l=min(l,a[i]);
r=max(r,a[i]);
}
l--;r++;
m=(n-1)%k+1;
while(l<r)
{
int mid=(l+r+1)>>1;
if(check(mid))
l=mid;
else r=mid-1;
}
cout<<l<<endl;
}
signed main()
{
IOS
int t;
cin>>t;
while(t--)
solve();
return 0;
}