Educational Codeforces Round 81 (Rated for Div. 2)(A~C)

题目地址
在这里插入图片描述
在这里插入图片描述
给出每个数字需要的笔画数,给出总的笔画数求问可以组成的最大数字
优先考虑数字的数量 在数量相同的时候找数最大的
很容易发现,1是两画中最大的,7是三画中最大的
所以n为偶数直接全为1可以保证数字数量最多,n为奇数先抽出3创造一个7再全变成1保证结果最大

void solve()
{
    int n=read();
    if(n%2==0)
    {
        n/=2;
        rep(i,1,n)
        {
            printf("1");
        }
        cout<<'\n';
    }
    else
    {
        printf("7");
        n-=3;
        n/=2;
        rep(i,1,n)
        {
            printf("1");
        }
        printf("\n");
    }
}

在这里插入图片描述
在这里插入图片描述
大概意思是给出一个字符串,你可以无限加倍一直延伸下去,
对于它的每个前缀中0的数目减去1的数目
等于x 共有多少个这样的前缀
若有无限个则输出-1
我们先开一个num数组来记录当前出现的0和1的数目之差
无限
在这种情况下,肯定是每一串都对后面的没有造成影响,所以原字符串中0和1的数目要保证一样来相互抵消,所以num[len]应该要等于0
然后我们寻找在这个字符串中是否出现过满足x的位置 是则无限,反之就是0

	if(num[n]==0)
    {
        bool flag=true;
        for(int i=1;i<=n&&flag;i++)
            if(num[i]==k)
                flag=false;
        if(!flag)
        {
            puts("-1");
            return ;
        }
        else
        {
            puts("0");
        }
    }

有限
有限的话我们假设在经历了n个循环后在n+1个循环找到了满足条件的位置,我们只需要统计位置的个数就好了
注意: 如果为0,那要先加1,题目要求空串也算
假设当前位置可以满足条件且已经经历过n个循环,那么
n*num[len]+num[pos]==x
所以我们遍历原字符串,当目前位置满足以下条件时

(x-num[pos])%num[len]==0

可满足
但是! 我们要考虑负数的情况,因为1%(-1)也为0,但是方向不同,不断远离不可能到达,所以要排除,我们在后面需要再加一个条件来判断符号是否相同

if((k-num[i])%num[n]==0&&(k-num[i])/num[n]>=0)

改成这样就好了
所以代码是

void solve()
{
    ms(num,0);
    n=read(),k=read();
    scanf("%s",str+1);
    rep(i,1,n)
    {
        if(str[i]=='0')
        {
            num[i]=num[i-1]+1;
        }
        else 
        {
            num[i]=num[i-1]-1;
        }
    }
    if(num[n]==0)
    {
        bool flag=true;
        for(int i=1;i<=n&&flag;i++)
            if(num[i]==k)
                flag=false;
        if(!flag)
        {
            puts("-1");
            return ;
        }
        else
        {
            puts("0");
        }
    }
    else
    {
        int ans=0;
        if(k==0){ans++;}
        rep(i,1,n)
        {
            if((k-num[i])%num[n]==0&&(k-num[i])/num[n]>=0)///要求同号
                ans++;
        }
        cout<<ans<<endl;
    }
}

在这里插入图片描述
给出一个模式串和目标串
从一个空串开始逐步变成目标串
每次可以选取模式串的子串排在空串后面,顺序不能变
求最小的次数
思路很明显,每一次遍历都找尽量长的子串,但是暴力找的话
1e5范围n2直接炸了…
比如这样

	int cnt=1;
    int st=0;
    len=tmp.size();
    int len1=ans.size(),pos=0;
    while(pos<len1)
    {
        bool flag=false;
        for(st=0;st<=len-1&pos<len1;st++)
        {
            if(tmp[st]==ans[pos])
                pos++,flag=true;
        }
        if(pos!=len1)
            cnt++;
        if(!flag)
        {
            cout<<-1<<endl;
            return ;
        }
    }
    cout<<cnt<<endl;

暴力查找肯定不行,我们可以优化for循环内部的那个循环,我们开个vector数组储存原字符串中每种字母的所有下标,比如

	vector<int>v[27];
    rep(i,0,len-1)
    {
        int pos=tmp[i]-'a'+1;
        v[pos].pb(i);
    }

利用STL库的upper_bound二分查找第一个符合条件的下标,找到后再更新下标
注意upper和lower这两个的区别

	pos=upper_bound(v[id].begin(),v[id].end(),pos)-v[id].begin();
        pos=v[id][pos];

每次没找到就从头再开始就好了并ans++
代码是

string tmp,ans;
void solve()
{
    cin>>tmp>>ans;
    int len=tmp.size();
    vector<int>v[27];
    rep(i,0,len-1)
    {
        int pos=tmp[i]-'a'+1;
        v[pos].pb(i);
    }
    int cnt=1,pos=-1;
    len=ans.size();
    rep(i,0,len-1)
    {
        int id=ans[i]-'a'+1;
        int num=v[id].size();
        if(num==0)
        {
            puts("-1");
            return ;
        }
        if(pos>=v[id][num-1])
        {
            pos=-1;
            cnt++;
            i--;
            continue;
        }
        pos=upper_bound(v[id].begin(),v[id].end(),pos)-v[id].begin();
        pos=v[id][pos];
        //cout<<pos<<endl;
    }
    cout<<cnt<<endl;
}

猜你喜欢

转载自blog.csdn.net/leoxe/article/details/105299040