C. Water the Trees
难度 1700
二分答案,算是二分答案里比较复杂的
根据题意
1.我们知道在一段时间里加高2的次数和加高1的次数是固定,所以我们可以选择长高2的时候应该优先考虑。
2.每个树需要长高的空间就是目标高度和这棵树高度的差值
3.目标高度可能是数组中最高的或者最高的+1(我求稳按照最高+3来的)
#include <bits/stdc++.h>
using namespace std;
#define int long long
int tree[300000+100];
int n;
bool check(int day,int maxx)
{
int sum=0;
int even=day/2;
int odd=(day+1)/2;
for(int i=1;i<=n;i++)
{
int dis=maxx-tree[i];
sum+=dis;
if(dis<=even*2)
{
even-=dis/2;
odd-=dis%2;
}
else
{
dis-=even*2;
even=0;
odd-=dis;
}
if(odd<0)
{
return false;
}
}
even=day/2;
odd=(day+1)/2;
if(sum>even*2+odd)
return false;
return true;
}
signed main()
{
int t;
for(cin>>t;t;t--)
{
cin>>n;
int ans=1e17+10;
for(int i=1;i<=n;i++)
{
cin>>tree[i];
}
sort(tree+1,tree+n+1);
for(int i=tree[n];i<=tree[n]+3;i++)
{
int l=0,r=1e17+7;
while(l<=r)
{
int mid=(l+r)/2;
if(check(mid,i))
{
r=mid-1;
}
else
{
l=mid+1;
}
}
ans=min(ans,l);
}
cout<<ans<<endl;
for(int i=1;i<=n;i++)
{
tree[i]=0;
}
}
}
D. Progressions Covering
难度 1900
贪心好题,线段树好题
题意:给定长度为n的数组,每次可以选择长度为k的一段区间减去长度为k的升序排列(1 2 3 4…n)
问最多用几次减去排列的操作能够让整个数组都小于等于0
思路一:从前往后减,我们发现第一个元素始终只能减去1,所以开始必定要先减掉b[1]个全排列,然后从前往后去找大于1的数,但是这种思路会发现在第一步减掉b[1]个全排列之后就会很难操作,我放弃了
思路二:从后往前减,如果从后往前,那么最后一个元素减的值每次为k,最多减(b[n]-sum)/k+((b[n]-sum)%k!=0)次全排列,对于第n位减掉的总值是次数*k,每次往后一位减掉的总值少k,当碰到减掉的总值少于这一位的数值时再次进行(b[i]-sum)/k+((b[i]-sum)%k!=0)次全排列,此时的sum就是之前操作对于第i位减掉的总值,然而这样还需要分1到k-1和k到n。
具体看代码。
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n,b[600005],k,op,sum,ans,a[600005];
signed main()
{
cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>b[i];
}
int ans=0;
for(int i=n;i>=k;i--)
{
if(b[i]>sum)
{
a[i]=(b[i]-sum)/k+((b[i]-sum)%k!=0);
sum+=k*a[i];
op+=a[i];
ans+=a[i];
}
sum-=op;
op-=a[i+k-1];
}
for(int i=k-1;i>=1;i--)
{
if(b[i]>sum)
{
a[i]=(b[i]-sum)/i+((b[i]-sum)%i!=0);
sum+=a[i]*i;
op+=a[i];
ans+=a[i];
}
sum-=op;
op-=a[i+k-1];
}
cout<<ans<<endl;
return 0;
}
P1650 田忌赛马
难度:提高
区间DP好题,贪心好题。
练练手罢
#include <bits/stdc++.h>
using namespace std;
int a[3000];
int b[3000];
bool cmp(int a,int b)
{
return a>b;
}
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
for(int j=1;j<=n;j++)
{
cin>>b[j];
}
sort(a+1,a+n+1,cmp);
sort(b+1,b+n+1,cmp);
int fa=1;
int ta=n;
int fb=1;
int tb=n;
int ans=0;
while(n--)
{
if(a[fa]>b[fb])
{
fa++;
fb++;
ans+=200;
}
else if(a[fa]<b[fb])
{
fb++;
ta--;
ans-=200;
}
else
{
if(a[ta]>b[tb])
{
ta--;
tb--;
ans+=200;
}
else
{
if(a[ta]<b[fb])
ans-=200;
ta--;
fb++;
}
}
}
cout<<ans<<endl;
return 0;
}
B. Getting Zero
这题我的做法感觉是非正解的,,但是没任何优化下跑了46m感觉还好
难度:1300
有趣的数学题
思路:预处理+寻找最小值
#include <iostream>
#include <algorithm>
using namespace std;
const int mod=32768;
int a[32768+100];
int num[32768+100];
int ans[32768+100];
signed main()
{
for(int i=1;i<=32768;i++)
{
int temp=i;
int cnt=0;
while(temp)
{
cnt++;
temp=(temp*2)%mod;
}
a[i]=cnt;
}
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>num[i];
ans[i]=a[num[i]];
for(int j=1;j<=15;j++)
{
if(num[i]+j>32768)
break;
ans[i]=min(ans[i],a[num[i]+j]+j);
}
ans[i]=min(ans[i],32768-num[i]);
}
for(int i=1;i<=n;i++)
{
cout<<ans[i]<<" ";
}
cout<<endl;
return 0;
}