A——Question Marks
统计A,B,C,D四个字符出现的的次数与每次的n取最小值后相加(多次询问记得清空num数组)
#include<bits/stdc++.h>
using namespace std;
int num[6];
void solve()
{
for(int i=0;i<=4;i++) num[i]=0;
int n,ans=0;
cin>>n;
string s;
cin>>s;
for(int i=0;i<s.size();i++)
{
if(s[i]=='?') continue;
num[s[i]-'A']++;
}
for(int i=0;i<=4;i++)
{
ans+=min(n,num[i]);
// cout<<num[i]<<'\n';
}
cout<<ans<<'\n';
}
int main()
{
int t;
cin>>t;
while(t--) solve();
return 0;
}
B——Parity and Sum
首先,按照题目要求,只能选奇偶不同的两个数相加,再将其中较小的那个数替换成两者的和,所以只能是是把偶数变成奇数,最好情况下每次操作都能把一个偶数变成更大的奇数,需要注意的是如果选的偶数比选的奇数大,那奇数就会变成更大的奇数,偶数不变,所以需要维护一个数组里奇数的最大值,首先先拿最大的奇数和偶数从小到大依次比较,如果当前奇数最大值大于当前遍历到的偶数,奇数最大值就要加上当前的偶数,如果比较成功则结果为cnt(偶数的个数),如果失败则为偶数个数+1(一开始就将最大奇数与最大偶数相加)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
ll p[N];
void solve()
{
int n,cnt=0;
ll ma=0;
cin>>n;
for(int i=1;i<=n;i++)
{
ll x;
cin>>x;
if(x%2==0) p[++cnt]=x;
else ma=max(x,ma);
}
sort(p+1,p+1+cnt);
if(cnt==n||cnt==0) cout<<0<<'\n';
else
{
int flg=0;
for(int i=1;i<=cnt;i++)
{
if(ma>=p[i]) ma+=p[i];
else
{
flg=1;
break;
}
}
if(flg) cout<<cnt+1<<'\n';
else cout<<cnt<<'\n';
}
}
int main()
{
int t;
cin>>t;
while(t--) solve();
return 0;
}
C——Light Switches
首先将P数组从小到大排序,再维护一个从1->n的全开灯区间 , 表示从1到i全开灯区间是
分析可知一共有三种情况
1.第个区间与第个区间相交
2.第个区间的等效区间与第个区间相交
3.第个区间的等效区间不在第个区间内
第一种情况
第二种情况 需要先算出等效区间
左端点
右端点
第三种情况直接返回-1
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
ll p[N];
void solve()
{
int n,t;
cin>>n>>t;
ll l=1,r=t;
int flg=0;
for(int i=1;i<=n;i++) cin>>p[i];
sort(p+1,p+1+n);
l=p[1],r=p[1]+t-1;
for(int i=2;i<=n;i++)
{
// cout<<l<<' '<<r<<'\n';
if(r>=p[i])
{
l=max(l,p[i]);
continue;
}
else
{
ll num=((p[i]-r+2*t-1)/(2*t));
// cout<<num<<'\n';
ll dl=l+2*t*num,dr=r+2*t*num;
if(dl<=p[i]+t-1)
{
l=max(p[i],dl);
r=min(p[i]+t-1,dr);
}
else
{
cout<<"-1\n";
return;
}
}
// cout<<l<<' '<<r<<'\n';
}
cout<<l<<"\n";
}
int main()
{
int t;
cin>>t;
while(t--) solve();
return 0;
}
D——Med-imize
参考题解CF1993D 题解 - 洛谷专栏 (luogu.com.cn)
首先二分中位数mid,选定中位数后将数组里大于中位数的置为1,小于中位数的置为-1,二分答案找删除个长度为m的连续区间后剩下的数的和的最大值,如果大于0则返回ture,小于0则返回false
方法一:记忆化搜索
:求解从1到u,删除k段长度为m的区间后所剩下的数的和的最大值
:表示从1到i,删除个长度为m的连续区间后剩下的数的和的最大值
记忆化搜索降低时间复杂度
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
typedef long long ll;
#define ios ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
ll p[N],d[N];
ll dp[N][2],vis[N][2];
int n,m;
ll dfs(ll u,ll k)
{
if (u<0||k<0||m*k>u) return -1e9;
if(!u) return 0;
int t;
t=k-(u-1)/m;
if(vis[u][t]) return dp[u][t];
vis[u][t]=1;
dp[u][t]=max(dfs(u-1,k)+d[u],dfs(u-m,k-1));
return dp[u][t];
}
bool check(ll u)
{
for(int i=1;i<=n;i++)
{
vis[i][0]=vis[i][1]=0;
if(p[i]<u) d[i]=-1;
else d[i]=1;
}
int num=(n-1)/m;
int cnt=dfs(n,num);
if(cnt>0) return 1;
return 0;
}
void solve()
{
ll l=1,r=0;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>p[i];
r=max(r,p[i]);
}
while(l<r)
{
ll mid=(l+r+1)/2;
if(check(mid)) l=mid;
else r=mid-1;
}
cout<<l<<'\n';
}
int main()
{
ios
int t;
cin>>t;
while(t--) solve();
return 0;
}
方法二:线性DP
表示尽可能多的删除长度为m的连续区间后剩下的数字(最多剩下m个)之和的最大值
using namespace std;
const int N=5e5+10;
typedef long long ll;
#define ios ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
ll p[N],d[N];
ll dp[N];
int n,m;
// ll dfs(ll u,ll k)
// {
// if (u<0||k<0||m*k>u) return -1e9;
// if(!u) return 0;
// int t;
// t=k-(u-1)/m;
// if(vis[u][t]) return dp[u][t];
// vis[u][t]=1;
// dp[u][t]=max(dfs(u-1,k)+d[u],dfs(u-m,k-1));
// return dp[u][t];
// }
bool check(ll u)
{
for(int i=1;i<=n;i++)
{
if(p[i]<u) d[i]=-1;
else d[i]=1;
}
for(int i=1;i<=n;i++)
{
if(i!=1&&(i-1)%m==0) dp[i]=max(dp[i-m],d[i]);
else
{
dp[i]=dp[i-1]+d[i];
if(i-1>m) dp[i]=max(dp[i],dp[i-m]);
}
}
if(dp[n]>0) return 1;
return 0;
}
void solve()
{
ll l=1,r=0;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>p[i];
r=max(r,p[i]);
}
while(l<r)
{
ll mid=(l+r+1)/2;
if(check(mid)) l=mid;
else r=mid-1;
}
cout<<l<<'\n';
}
int main()
{
ios
int t;
cin>>t;
while(t--) solve();
return 0;
}