Codeforces Round #558 (Div. 2) B1B2D 题解

感觉最近找到状态了?(反手就是fst)

在这里插入图片描述
B1. Cat Party (Easy Edition)

题意:有最多10种颜色,你要求一个最大的x,使得在前x个颜色中移除某个颜色,所有出现过的颜色出现次数相等。
思路:用十个前缀和数组分别保存10种颜色出现次数的前缀和,暴力枚举x,然后记录10种颜色出现次数,然后排个序,如果只有一种颜色 或者 只有一种颜色出现一次其他颜色出现次数相同 或者 只有一种颜色出现k次其他颜色全都出现k-1次,那么把x更新到答案。
#include<bits/stdc++.h>
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<bits/stdc++.h>
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<bits/stdc++.h>
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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长沙橘子猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值