- CF1198A MP3
- CF486C Palindrome Transformation
- CF467C George and Job
- CF1228C Primes and Multiplication
- CF1279C Stack of Presents
- CF1238C Standard Free2play
- CF1223C Save the Nature
1.CF1198A MP3
题意有点绕
总需要的存储空间为
n
∗
log
2
t
n*\log_{2} t
n∗log2t,
t
t
t为不同音量的数量
现有的存储空间为 m ∗ 8 m*8 m∗8
要选择一个区间使所有的的音量值都变为区间之内,使之可以储存下来,最小的被更改的强度值的数量
先排序,并求出 t t t和每一个音量的数量, m ∗ 8 m*8 m∗8
可以得出 log 2 t ≤ m n \log_{2} t\leq\frac{m}{n} log2t≤nm,我们需要找到一个符合条件的 t t t又要使被更改的强度值的数量最小,要怎么办呢?用双指针,让不同音量数量在 m a x x maxx maxx范围内,并且记录最多同时有多少个音量在队列中,最终答案即为 n − a n s n-ans n−ans
#include<bits/stdc++.h>
using namespace std;
int n,m,k,ans,c,maxx;
void fuck();
inline int read()
{
int x=0,k=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-')k=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*k;
}
int a[400005],b[400005];
bool cmp(int x,int y){
return x>y;
}
vector<int>v;
signed main(){
cin>>n>>m;
m*=8;
for(int i=1;i<=n;i++){
a[i]=read();
}
sort(a+1,a+1+n,cmp);
int num=0;
int t=1;
for(int i=1;i<=n;i++){
if(a[i]!=a[i-1]){
num++;
b[t]=num;
t++;
num=0;
}
else num++;
}
maxx=m/n;
if(log2(t)<=maxx){
cout<<"0";
return 0;
}
maxx=pow(2,maxx);
// for(int i=1;i<=t+maxx;i++){
// b[i]+=b[i-1];
// }
ans=0;
queue<int>q;
for(int i=1;i<=t;i++){
q.push(b[i]);
c+=b[i];
if(q.size()>maxx){
int x=q.front();
c-=x;
q.pop();
}
ans=max(ans,c);
}
cout<<n-ans;
}
2.CF486C Palindrome Transformation
有四种操作,光标左移,右移,将光标所在的字母++ or - -
问最少要多少次操作能使其变为回文串
直接贪心,记录下最左端需要修改的字母和最右端需要修改的字母
讨论一下这样的正确性,从下标 0 0 0开始遍历,达到的第一个需要改的即为 l l l,最后一个修改的位置为 r r r,最终光标移动的总次数为
ans+=min(abs(p-r),abs(p-l));
ans+=max(r-l,0);
模拟一下就能明白是什么意思
例如光标初始位置为 p = 5 p=5 p=5, l l l为2, r r r为7,先加上 7 − 5 7-5 7−5,即为到r的距离,但是光标总是需要从l走到r这个位置的,所以需要加上 r − l r-l r−l
对应位置的字母的改法就比较简单了
完整代码:
#include<bits/stdc++.h>
using namespace std;
string a;
int n,p,l=-1,r;
int main(){
cin>>n>>p;
cin>>a;
n--;
p--;
long long ans=0;
if(p>n/2)p=n-p;
for(int i=0;i<=n/2;i++){
if(a[i]!=a[n-i]){
int s=abs(a[i]-a[n-i]);
ans+=min(s,26-s);
if(l<0)l=i;
else r=i;
}
}
ans+=min(abs(p-r),abs(p-l));
ans+=max(r-l,0);
if(l<0)cout<<"0";
else cout<<ans;
}
3.CF467C George and Job
n个数,选出k组,每组m个数,使选出的数的和值最大
先求出前缀和
d p [ i ] [ j ] dp[i][j] dp[i][j]代表前 i i i个数选 j j j个区间可以获得的最大值
转移方程: d p [ i ] [ j ] = m a x ( d p [ i ] [ j ] , d p [ i − m ] [ j − 1 ] + s u m [ i ] − s u m [ i − m ] ) ; dp[i][j]=max(dp[i][j],dp[i-m][j-1]+sum[i]-sum[i-m]); dp[i][j]=max(dp[i][j],dp[i−m][j−1]+sum[i]−sum[i−m]);
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read()
{
int x=0,k=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-')k=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*k;
}
int n,m,k;
int a[5005],sum[5005],dp[5005][5005];
signed main(){
cin>>n>>m>>k;
for(int i=1;i<=n;i++){
a[i]=read();
sum[i]=a[i];
}
for(int i=1;i<=n;i++){
sum[i]+=sum[i-1];
}
for(int i=1;i<=n;i++){
for(int j=1;j<=k;j++){
dp[i][j]=dp[i-1][j];
if(i>=m){
dp[i][j]=max(dp[i][j],dp[i-m][j-1]+sum[i]-sum[i-m]);
}
}
}
cout<<dp[n][k];
return 0;
}
4.CF1228C Primes and Multiplication
数论,咕咕咕
5.CF1279C Stack of Presents
放回来的时候可以任意顺序,维护最大深度即可
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int tt,n,m,a[maxn],b[maxn],num[maxn];
int main(){
cin>>tt;
while(tt--){
long long ans=0;
cin>>n>>m;
for(int i=1;i<=n;i++){
int x;
cin>>x;
a[x]=i;
}
int dep=0;
for(int i=1;i<=m;i++){
int x;
cin>>x;
if(a[x]>dep){
ans+=(a[x]-i)*2+1;
dep=a[x];
}
else ans++;
}
cout<<ans<<endl;
}
return 0;
}
6.CF1238C Standard Free2play
给出 n n n个已经选中的台阶
初始时有 n n n 个平台为被选中,保证平台 h h h 被选中,您每次可以进行一个操作,不妨假设您当前站在平台 x x x 处(此时平台 x x x 一定被选中),即让平台 x x x变成未被选中,而平台 x − 1 x - 1 x−1变成相反的状态。
如果落差大于1,不管这个数字是多少都是一样的,需要一个魔法水晶
比如选中在1000,999和1是打开的,关闭1000的时候999也会关闭,用水晶关闭999这一层才能到达999,然后关闭999,998变为打开状态,关闭998,997又变成打开 . . . ... ...
#include<bits/stdc++.h>
using namespace std;
int t,h,n,a[200005];
int main(){
cin>>t;
while(t--){
cin>>h>>n;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
int ans=0;
a[n+1]=0;
for(int i=2;i<=n+1;i++){
if(a[i]-a[i+1]>1)ans++;
else i++;
}
cout<<ans<<endl;
}
return 0;
}
7.CF1223C Save the Nature
咕了,待补