2022 Jiangsu Collegiate Programming Contest(ACIJKL)

目录

A. PENTA KILL!(暴力,枚举)

C. Jump and Treasure(单调队列优化dp)

I. Cutting Suffix(签到,思维)

J. Balanced Tree(推式子,打表找规律好像可以写)

K. aaaaaaaaaaA heH heH nuN(二进制,模拟)

L. Collecting Diamonds(思维,模拟,贪心)


补题日记,记录一下训练.

本场链接

Dashboard - 2022 Jiangsu Collegiate Programming Contest - CodeforcesCodeforces. Programming competitions and contests, programming communityhttps://codeforces.com/gym/103743

A. PENTA KILL!(暴力,枚举)

签到,一开始还想用map,实际上看见了1000复杂度,直接n^2复杂度,暴力跑即可.只要对每一个击杀者,和他后面的被他击杀的人进行遍历,当有连续的五个不同的,就完成了PENTA KILL!

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N =5e5+10,mod=998244353;
string s[1004],s1[1004];
void solve()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>s[i]>>s1[i];
    for(int i=1;i<=n;i++)
    {
        map<string,int>ma;
        int cnt=0;
        for(int j=i;j<=n;j++)
        {
            if(s[j]==s[i])
            {
                if(ma[s1[j]]==0)
                {
                    cnt++;
                    ma[s1[j]]=1;
                }
                else
                    break;
                if(cnt==5)
                {
                    cout<<"PENTA KILL!\n";
                    return ;
                }
            }
        }
    }
    cout<<"SAD:(\n";
    return;
}
signed main()
{
    solve();
    return 0;
}

C. Jump and Treasure(单调队列优化dp)

单调队列优化dp的板子题.一开始看题,很容易想到n^2的做法,

                \large f[ i ]=max(f[i],\sum_{i=j-p}^{j-1}f[j])       

数据范围给的是1e6,n^2显然不可以,直接考虑单调队列优化去优化这个第二层循环.思路就是用一个宽度为p的单调队列去存已经遍历过的函数值,当存储的函数值不在这个范围内就从队列里面去掉,因为是单调队列,我们从队首取的就应该是最大值,直接每次状态转移取这个最大值.当前的函数元素就从队尾进入,保证队列单调,每次取max救救优化到了o1复杂度.

#include<bits/stdc++.h>
#define int long long
using namespace std;
int f[1000006];
int a[1000006];
int qu[1000006];
vector<int>b;
void solve()
{
    int n,q,p,x;
    scanf("%lld%lld%lld",&n,&q,&p);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    while(q--)
    {
        b.clear();
        scanf("%lld",&x);
        for(int i=0;i<=n;i+=x)
            b.push_back(i);
        b.push_back(n+1);
        int hh=0,tt=0;
        qu[hh]=0;
        f[0]=0;
        for(int i=0;i<b.size();i++)
        {
            while(hh<=tt&&b[i]-qu[hh]>p)
                hh++;
            f[b[i]]=a[b[i]]+f[qu[hh]];
            while(hh<=tt&&f[qu[tt]]<=f[b[i]])
                tt--;
            qu[++tt]=b[i];
        }
        if(x>p)
        {
            printf("Noob\n");
            continue;
        }
        else
            printf("%lld\n",f[n+1]);
    }
    return ;
}
signed main()
{
    solve();
    return 0;
}

I. Cutting Suffix(签到,思维)

签到,一开始看着还以为是什么字符串题.仔细一想,只要存在不相等的两个相邻字母,我们从这两者中间切开,索取的后缀的LCP一定为0,最小.如果全部都相等的话,我们就把一个字母和n-1个字母切开,此时LCP为1.

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N =5e5+10,mod=998244353;
void solve()
{
    string s;
    cin>>s;
    set<char>ss;
    for(int i=0;i<s.size();i++)
        ss.insert(s[i]);
    if(ss.size()==1)
        cout<<s.size()-1<<"\n";
    else
        cout<<"0\n";
    return ;
}
signed main()
{
    solve();
    return 0;
}

J. Balanced Tree(推式子,打表找规律好像可以写)

存在一颗二叉树,问左右节点相差不大于1的有多少种情况.

推式子的题.

 最后直接跑dfs改变参数即可,结果为为最后x=1的时候的c,求2^c即可(c>=64直接输出0)

#include<bits/stdc++.h>
#define int unsigned long long int
using namespace std;
int dfs(int x,int a,int b,int c)
{
    if(x==0)
        return 0;
    else if(x==1)
        return c;
    else if(x&1)
        return dfs(x>>1,a*2+b,b,b+c);
    else 
        return dfs(x>>1,a,a+2*b,a+c);
}
void solve()
{
    int x;
    cin>>x;
    int c=dfs(x,1ull,0ull,0ull);
    if(c>=64)
        cout<<"0\n";
    else
        cout<<(1ull<<c)<<"\n";
    return ;
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin>>t;
    while(t--)
       solve();
    return 0;
}

K. aaaaaaaaaaA heH heH nuN(二进制,模拟)

我们先取前缀的nunhehhe这一段,设在这一段后面,每一个h之后存在有x个a,那么就会为答案增加上2^x-1的贡献(有这么多种子串取法).如果能够变成2^x的话就会就会想到用二进制的方式来进行组合,那么我们只需要在最后一个a前面加一个h,就可以满足当这个h后面有x个a的情况时,加上最后一个a前面的贡献,就是2^x的贡献,那么我们就可以用二进制模拟的方式来确定h的位置,首先先输出nunhehhe这个前缀.之后在后面加上32个a,从右往左的a和a之间的空隙就是题目所给的数字的二进制位(从第0位开始),只需要对原数字二进制位数上为1的对应位置插入一个h,在再最后一个可以插入的位置插入h,就可以为答案贡献2^x.

#include<bits/stdc++.h>
#define int long long
using namespace std;
int cnt[42];
int pow2[40];
void init()
{
    pow2[0]=1;
    for(int i=1;i<=31;i++)
        pow2[i]=pow2[i-1]*2;
        return;
}
void solve()
{
    int n,ans=0;
    cin>>n;
    for(int i=31;i>0;i--)
        cnt[i]=0;
    for(int i=31;i>0;i--)
    {
        if(n>=pow2[i])
        {
            cnt[i]++;
            n-=(pow2[i]);
            cnt[1]++;
        }
    }
    if(n%2)
        cnt[1]++;
    cout<<"nunhehhe";
    for(int i=31;i>0;i--)
    {
        for(int j=0;j<cnt[i];j++)
            cout<<"h";
        cout<<"a";
    }
    cout<<"\n";
    return ;
}
signed main()
{
    init();
    int t;
    cin>>t;
    while(t--)
       solve();
    return 0;
}

L. Collecting Diamonds(思维,模拟,贪心)

思路很妙.

对于每个字符B,我们删除之后都会改变后方的奇偶性,对于AC我们就算是删除了也不会改变其奇偶性.所以我们先把字符串分成很多段,每一段都是一个回文串:

AAA.....B.....CCC

针对于每一段,当我前面删除了k个B之后,因为奇偶性的改变我们可以一直对于AC进行删除.假设之前删除了三次B,那么当此时的B的位置为偶数时,我们就随着就的变化,最多可以删除三次.

要注意的是,我们每次必定可以删除完B,但是AC不一定,要考虑前面删除的B的个数(即为当前B的位置奇偶性改变的次数)

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
vector<pii>ve;
void solve()
{
    string s;
    cin>>s;
    for(int i=0;i<s.size();i++)
    {
        int l=i,r=i; 
        if(s[i]=='B')
        {         
            while(l-1>=0&&s[l-1]=='A')
                l--;
            while(r+1<s.size()&&s[r+1]=='C')
                r++;
            if(min(i-l,r-i)>0)
                ve.push_back({min(i-l,r-i),(i+1)%2});
        }
        i=r;
    }
    int ans=0,cntb=0;
    for(int i=0;i<ve.size();i++)
    {
        if(cntb==0)//我还没有删除b
        {
            if(ve[i].second==0)//此时的B在偶数位置(A在奇数上)上,只能删除AC
            {
                if(ve[i].first==1)//只有一段AC,只能删除AC
                    ans++;
                else//删完AC了还可以删除B
                    ans+=2,cntb++;
            }
            else//此时B在奇数位置(A在偶数位置),只能删除B
                cntb++,ans++;
        }
        else//当删除过B了,我每次可以进行删除的最多的情况就是和b删除次数有关
一般情况下如果我的A在奇数位置,我实际上估值可以删除一次AC,但是当前面有b进行删除时
后面就会受影响,位置奇偶性改变,又可以在进行删除,所以可以删除的最多次数就是
cntb+1次,这是针对于AC的,然后记得把B删了.还有AC的数量有限,还要取个min
        {
            ans+=min(ve[i].first,(ve[i].second%2==0)+cntb+1);
            cntb++;
        }
    }
    cout<<ans<<"\n";
    return ;
}
signed main()
{
    solve();
    return 0;
}

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值