题意:
给定长为n的数组,I个字节的硬盘
每个字节有8bit,这个硬盘要装下所以数组,每个不一样的数组占一个bit
让数组的所以值都在一个(l,r)范围内,超过的要修改,求最小的修改数目
解析:
k=I*8/n,如果k>=n||k>=cnt(不同的数目),直接输出0.否则计算pow(2,k)
先排序,然后求前缀和,直接贪心数目最大的(l,l+k-1)
ac:
#include<bits/stdc++.h>
#define MAXN 1000005
#define ll long long
using namespace std;
ll a[MAXN];
ll sum[MAXN];
int main()
{
ll n,I,k;
scanf("%lld%lld",&n,&I);
for(ll i=1;i<=n;i++)
scanf("%lld",&a[i]);
I=I*8;
k=I/n;
if(k>20)
{
printf("0\n");
}
else{
ll kk=pow(2,k);
if(kk>=n)
{
printf("0\n");
return 0;
}
sort(a+1,a+n+1);
ll cnt=1;
for(ll i=2;i<=n;i++)
{
if(a[i]>a[i-1])
sum[++cnt]=1+sum[cnt-1];
else sum[cnt]++;
}
if(kk>=cnt)
{
printf("0\n");
return 0;
}
ll ans=0;
for(ll i=1;i<=cnt;i++)
{
ll v=sum[i+kk-1]-sum[i-1];
ans=max(ans,v);
}
printf("%lld\n",n-ans);
}
return 0;
}
http://codeforces.com/contest/1200/problem/D
给定一个n*n棋盘,上面有若干个黑点,剩下的全是白点,你可以选择一个k*k的矩形涂白
问最多可以让白线的数目是多少(横白线+竖白线)
解析:
先预处理每个点向左||向下涂k格是否产生白线
然后用处理出来的在左前缀和,转化为二维的
前缀和预处理
sumL[i][j]//左边有多少个B
sumU[i][j]//上边有多少个B
R[i][j]//在(i,j)处右k个格子涂白,是否会产生白色横线
D[i][j]//在(i,j)处下k个格子涂白,是否会产生白色竖线
ansR[i][j]//在(i,j)处涂,产生白色横线的数目
ansD[i][j]//在(i,j)处涂,产生白色竖线的数目
ac:
#include<bits/stdc++.h>
#define MAXN 2005
using namespace std;
char pp[MAXN];
int sumL[MAXN][MAXN];//左边有多少个B
int sumU[MAXN][MAXN];//上边有多少个B
int R[MAXN][MAXN];//在(i,j)处右k个格子涂白,是否会产生白色横线
int D[MAXN][MAXN];//在(i,j)处下k个格子涂白,是否会产生白色竖线
int ansR[MAXN][MAXN];//在(i,j)处涂,产生白色横线的数目
int ansD[MAXN][MAXN];//在(i,j)处涂,产生白色竖线的数目
void out(int x[MAXN][MAXN],int n)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
cout<<x[i][j]<<" ";
cout<<endl;
}
cout<<endl;
}
int main()
{
int n,k;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%s",pp+1);
for(int j=1;j<=n;j++)
sumL[i][j]=sumL[i][j-1]+(pp[j]=='B');
for(int j=1;j<=n;j++)
sumU[i][j]=sumU[i-1][j]+(pp[j]=='B');
}
//out(sumU,n);
int ans=0;//原本就有的白线
for(int i=1;i<=n;i++)
if(sumL[i][n]==0)
ans++;
for(int i=1;i<=n;i++)
if(sumU[n][i]==0)
ans++;
for(int i=1;i<=n;i++)//预处理横线
{
for(int j=1;j-1+k<=n;j++)
{
if(sumL[i][n]==0)
continue;
if(sumL[i][n]==sumL[i][j-1+k]-sumL[i][j-1])//所以的黑点,全在这个可以涂的区间里
R[i][j]=1;
}
}
for(int i=1;i-1+k<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(sumU[n][j]==0)
continue;
if(sumU[n][j]==sumU[i-1+k][j]-sumU[i-1][j])
D[i][j]++;
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
ansR[i][j]=ansR[i-1][j]+R[i][j];//横线向下
ansD[i][j]=ansD[i][j-1]+D[i][j];//竖线向右
}
}
int maxs=0;
for(int i=1;i-1+k<=n;i++)
{
for(int j=1;j-1+k<=n;j++)
{
maxs=max(maxs,ansR[i-1+k][j]-ansR[i-1][j]+ansD[i][j-1+k]-ansD[i][j-1]);
}
}
printf("%d\n",ans+maxs);
return 0;
}
百度之星-1002(子串查询)
用前缀和解决这道题目
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 100005
using namespace std;
int x[maxn][27];
char str[maxn];
int main()
{
int t,n,q,l,r,cas=1,len;
scanf("%d",&t);
while(t--)
{
memset(x,0,sizeof(x));
printf("Case #%d:\n",cas++);//用前缀和解决
scanf("%d%d",&n,&q);
scanf("%s",str+1);
len=strlen(str+1);
for(int i=1;i<=len;i++)
{
for(int j=0;j<26;j++)
{
x[i][j]=x[i-1][j];
if((str[i]-'A')==j) x[i][j]++;//如果存在这个字母,就计数一次
}
}
for(int i=0;i<q;i++)
{
scanf("%d%d",&l,&r);
for(int j=0;j<26;j++)
if(x[r][j]-x[l-1][j])
{
printf("%d\n",x[r][j]-x[l-1][j]);
break;
}
}
}
return 0;
}