题意:有最多10种颜色,你要求一个最大的x,使得在前x个颜色中移除某个颜色,所有出现过的颜色出现次数相等。
思路:用十个前缀和数组分别保存10种颜色出现次数的前缀和,暴力枚举x,然后记录10种颜色出现次数,然后排个序,如果只有一种颜色 或者 只有一种颜色出现一次其他颜色出现次数相同 或者 只有一种颜色出现k次其他颜色全都出现k-1次,那么把x更新到答案。
#include
using namespace std;
const int maxn=100005;
int sum[11][maxn],a[11];
int ok(int x)
{
int sz=0;
for(int i=1;i<=10;i++)
if(sum[i][x]!=0)
{
a[++sz]=sum[i][x];
}
sort(a+1,a+1+sz);
if(sz==1)
return 1;
if(a[1]==1&&a[2]==a[sz])
return 1;
if(a[1]==a[sz-1]&&a[1]==a[sz]-1)
return 1;
return 0;
}
int main()
{
int n,x;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
for(int j=1;j<=10;j++)
sum[j][i]=sum[j][i-1];
sum[x][i]++;
}
int ans=0;
for(int i=n;i;i--)
if(ok(i))
{
ans=i;
break;
}
cout<<ans;
}
B2. Cat Party (Hard Edition)
题意:同上,颜色数量改为至多1e5
思路:,用一颗权值线段树记录每种颜色出现次数的最大值和最大值 x 最大值数量,最小值和最小值 x 最小值数量,然后根据这四个信息用上个题的判断方法即可。
#include
using namespace std;
const int maxn=100005;
int mx[maxn*4],mn[maxn*4],sum1[maxn*4],sum2[maxn*4];
void up(int o,int l,int r,int k)
{
if(l==r)
{
mx[o]++;
mn[o]=mx[o];
sum1[o]++;
sum2[o]++;
return;
}
int m=(l+r)/2,ls=o*2,rs=o*2+1;
if(k<=m)
up(ls,l,m,k);
else
up(rs,m+1,r,k);
if(mx[ls]>mx[rs])
mx[o]=mx[ls],sum1[o]=sum1[ls];
else if(mx[ls]<mx[rs])
mx[o]=mx[rs],sum1[o]=sum1[rs];
else
mx[o]=mx[ls],sum1[o]=sum1[ls]+sum1[rs];
if(!mn[ls])
mn[ls]=1000000;
if(!mn[rs])
mn[rs]=1000000;
if(mn[ls]<mn[rs])
mn[o]=mn[ls],sum2[o]=sum2[ls];
else if(mn[ls]>mn[rs])
mn[o]=mn[rs],sum2[o]=sum2[rs];
else
mn[o]=mn[ls],sum2[o]=sum2[ls]+sum2[rs];
}
set<int>s;
int ok(int n)
{
//if(sz==1)
if(s.size()==1)
return 1;
if(sum1[1]==n&&mx[1]==1)
return 1;
//if(a[1]==1&&a[2]==a[sz])
if(sum2[1]==1&&sum1[1]+sum2[1]==n)
return 1;
//if(a[1]==a[sz-1]&&a[1]==a[sz]-1)
if(mx[1]==mn[1]+1&&sum1[1]==mx[1]&&sum1[1]+sum2[1]==n)
return 1;
return 0;
}
int main()
{
int n,x,ans=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
up(1,1,100000,x);
s.insert(x);
if(ok(i))
ans=i;
}
cout<<ans;
}
D. Mysterious Code
题意:给你一个c串,你可以用任意小写字母替换c串中的 * 字符得到一个c`,给你一个s和t串,定义:f(s1,s2) 为字符串 s2 在 s1 中出现的次数 ,求最大的 ans=f(c`,s) - f(c`,t)。
题意: 设d[ i ][ j ][ k ]为c`串前 i 个字符匹配到了 s 的第 j 个字符,t 的第 k 个字符所能得到的最大ans,对于 c[i+1]串,我们分两类,如果c[i+1] == ‘*’,那么我们枚举26个字母,使用kmp算每个字母在 s 串和 t 串新的匹配的位置 p ,q,记flag=0,如果枚举的这个字母到了 s 串的末尾,flag+=1,如果到了 t 串的末尾,flag-=1,然后更新d[i+1][p][q]=max(d[i+1][ p ][ q ], d[ i ][ j ][ k ]+flag),如果c[ i+1 ] != ‘*’,那么只要计算c[ i+1 ]得到新的匹配就行了。
#include
using namespace std;
const int maxn=1005,inf=1e8;
int d[maxn][55][55];
char s[55],t[55],c[maxn];
int Next1[55],Next2[55];
void init()
{
int i,j,len=strlen(s);
Next1[0]=-1;
for(i=0,j=-1;i<len;)
if(j==-1||s[i]==s[j])
Next1[++i]=++j;
else
j=Next1[j];
len=strlen(t);
Next2[0]=-1;
for(i=0,j=-1;i<len;)
if(j==-1||t[i]==t[j])
Next2[++i]=++j;
else
j=Next2[j];
}
void up(int &x,int y)
{
x=max(x,y);
}
int get1(int k,char a)
{
while(1)
{
if(s[k]==a||k==-1)
return k+1;
else
k=Next1[k];
}
}
int get2(int k,char a)
{
while(1)
{
if(t[k]==a||k==-1)
return k+1;
else
k=Next2[k];
}
}
int main()
{
int ans=-inf;
scanf("%s%s%s",c+1,s,t);
init();
int n=strlen(c+1),m1=strlen(s),m2=strlen(t);
for(int i=0;i<=n;i++)
for(int j=0;j<m1;j++)
for(int k=0;k<m2;k++)
d[i][j][k]=-inf;
d[0][0][0]=0;
for(int i=0;i<n;i++)
for(int j=0;j<m1;j++)
for(int k=0;k<m2;k++)
if(d[i][j][k]!=-inf)
{
int p,q,flag=0;
if(c[i+1]!='*')
{
p=get1(j,c[i+1]);
q=get2(k,c[i+1]);
if(p==m1)
flag++,p=Next1[p];
if(q==m2)
flag--,q=Next2[q];
up(d[i+1][p][q],d[i][j][k]+flag);
//printf("flag=%d i=%d p=%d q=%d,dp=%d\n",flag,i+1,p,q,d[i+1][p][q]);
}
else
{
for(int a='a';a<='z';a++)
{
flag=0;
p=get1(j,a);
q=get2(k,a);
if(p==m1)
flag++,p=Next1[p];
if(q==m2)
flag--,q=Next2[q];
up(d[i+1][p][q],d[i][j][k]+flag);
}
}
}
for(int i=0;i<m1;i++)
for(int j=0;j<m2;j++)
up(ans,d[n][i][j]);
cout<<ans<<endl;
}