目录
A. Frog Jumping
B. Disturbed People
C. Good Array
D. Cutting Out
E. Thematic Contests
F1. Pictures with Kittens (easy version)
F2. Pictures with Kittens (hard version)
A. Frog Jumping
直接模拟即可注意数据范围需要开long long
void solve(){
LL a,b,k;
cin>>a>>b>>k;
cout << (k+1)/2*a - k/2*b << endl;
return ;
}
B. Disturbed People
简单贪心我们可以发现如果对于一个 1 0 1 那么我调整最后一个1 变为0 是最好的因为这样让后面也只会减少贡献所以调整后面的1即可
void solve(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
int ans = 0;
for(int i=1;i<=n-2;i++){
if(a[i] and !a[i+1] and a[i+2]){
ans ++ ;
a[i+2]=0;
}
}
cout << ans << endl;
return ;
}
C. Good Array
直接按照题目意思模拟即可,同时注意到如果删除的数恰好是后面总和的一半的时候需要特判
void solve(){
map<LL,int> mp;
cin>>n;
LL sum =0;
for(int i=1;i<=n;i++){
cin>>a[i];
mp[a[i]]++;
sum += a[i];
}
vector<int> ans;
for(int i=1;i<=n;i++){
LL now = sum -a[i];
if(now%2==0 and mp.count(now/2)){
if(a[i]*2==now){
if(mp[a[i]]>=2) ans.push_back(i);
}
else ans.push_back(i);
}
}
cout << ans.size() << endl;
for(auto&v:ans) cout << v << ' ';
cout << endl;
return ;
}
D. Cutting Out
首先我们可以简单的发现是具有二分性质的,然后我们就可以二分数量,check函数对一个数的个数整除即可,用map记录
bool check(int x){
int cnt = 0;
for(auto&[v,w]:mp) cnt += w/x;
return cnt>=m;
}
void solve(){
cin>>n>>m;
for(int i=1;i<=n;i++){
int x; cin>>x;
mp[x]++;
}
int l=1,r=n;
while(l<r){
int mid=l+r+1>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
vector<int> res;
for(auto&[v,w]:mp){
int t = w/l;
while(t--) res.push_back(v);
}
for(int i=0;i<m;i++) cout<<res[i]<<' ';
cout<<endl;
return ;
}
E. Thematic Contests
注意厘清题目意思,对于每一个数我们只在乎的是它的数量,然后对于题目意思的翻倍处理可以明显的知道最多只会处理20次以内,所以对于整个数的来取得的话我们直接看后20位即可,同时对于开头第一个数肯定是 1-smax,记录一下即可,简单判断时间复杂度是可行的
map<LL,int> mp;
int check(int x){
int sum = 0;
for(int i=max(1,n-20);i<=n;i++){
if(a[i]>=x){
sum += x;
x *= 2;
}
}
return sum;
}
void solve(){
cin>>n;
for(int i=1;i<=n;i++){
int x; cin>>x;
mp[x]++;
smax=max(smax,mp[x]);
}
for(auto&[v,w]:mp){
a[++m]=w;
}
sort(a+1,a+1+m);
int ans = 1;
for(int i=1;i<=smax;i++){
ans = max(ans,check(i));
}
cout << ans << endl;
return ;
}
F1. Pictures with Kittens (easy version)
我们可以明显的看出这是最优性问题,我们考虑dp,首先定义状态,首先第一是数量,第二维就是当前整个数选的是什么位置,同时转移来源是对于当前要选的数的前k个以内的,所以转移是ok的,同时注意求最大值,也就是一开始有些是不可以转移的,所以定义为负无穷即可
LL dp[M][M];
int w[M];
void solve(){
cin>>n>>k>>m;
for(int i=1;i<=n;i++) cin>>w[i];
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
dp[i][j]=-1e18;
dp[0][0]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=i and j<=m;j++)
for(int u=max(i-k,0);u<i;u++){
dp[j][i]=max(dp[j][i],dp[j-1][u]+w[i]);
}
LL ans = -1;
for(int i=n-k+1;i<=n;i++) ans = max(ans,dp[m][i]);
cout << ans << endl;
return ;
}
F2. Pictures with Kittens (hard version)
依照上一题的分析同时我们注意到数据范围也就是只能是用的时间复杂度来通过本题,我们在上题的分析基础上分析,可以发现dp[i](选的第i个数的转移来源 一定是选的第i-1个数)所以考虑对此优化,当我选第i个数的时候选到第j个位置时,后面的选到第j+1的数只能由dp[i-1][(max(0,j-k),j]组成这个状态的变化可以通过在转移j的同时用单调队列优化上一层的状态,这样就是满足要求的时间复杂度
LL dp[M][M];
int w[M];
void solve(){
cin>>n>>k>>m;
for(int i=1;i<=n;i++) cin>>w[i];
if(n/k>m){
cout<<-1<<endl;
return ;
}
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
dp[i][j]=-1e18;
dp[0][0]=0;
for(int i=1;i<=m;i++){
deque<int> q;
q.push_back(0);
for(int j=1;j<=n;j++){
while(!q.empty() and j-q.front()>k) q.pop_front();
dp[i][j]=dp[i-1][q.front()]+w[j];
while(!q.empty() and dp[i-1][j]>=dp[i-1][q.back()]) q.pop_back();
q.push_back(j);
}
}
LL ans = -1;
for(int i=n-k+1;i<=n;i++) ans = max(ans,dp[m][i]);
cout << ans << endl;
return ;
}
常见的dp从都是依靠转移的状态是不是只有上一层,然后通过记录上一层的信息来做直接转移而优化一层循环