2022牛客寒假算法基础集训营4

A-R

长度为n的字符串,只包含大写字母,问有多少段连续子串能满足包含至少k个“R”且不包含“P”。(1n200000,1k20)

思路

由于不要“P”,所以就很容易想到在每一段没有“P”的串中尺取,然后就结束了。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
char s[1000005];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    scanf("%s",s+1);
    int pre=1;
    ll ans=0;
    for(int i=1;i<=n+1;i++)
    {
        if(i==n+1||s[i]=='P')
        {
            int l=pre,r=pre;
            int num=(s[l]=='R');
            while(r<i)
            {
                if(num<m)
                {
                    r++;
                    num+=(s[r]=='R');
                }
                if(num>=m)
                {
                    ans+=(i-r);
                    num-=(s[l]=='R');
                    l++;
                }
            }
            pre=i+1;
        }
    }
    printf("%lld\n",ans);
    return 0;
}

B-进制

长度为n的字符串,q组操作,每次输入3个整数op,x,y。若op1,则将第x个数字改为y;若op2,查询区间[x,y]所能表示某进制的最小值(进制必须合法且在二进制到十进制之间,可包含前导0),对1e9+7取模。(1n,q105)

思路

敲十棵线段树应该就能解决问题了吧(逃

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
char s[100005];
ll k[100005];
ll tree[11][400005];
ll poww(ll x,ll n)
{
    ll ans=1;
    while(n)
    {
        if(n&1)(ans*=x)%=mod;
        (x*=x)%=mod;
        n>>=1;
    }
    return ans;
}
void pushup(int p,int l,int r)
{
    int mid=(l+r)>>1;
    tree[1][p]=max(tree[1][p<<1],tree[1][p<<1|1]);
    for(int i=2;i<=10;i++)
    {
        tree[i][p]=(tree[i][p<<1]*poww(i,r-mid)%mod+tree[i][p<<1|1])%mod;
    }
}
void build(int p,int l,int r)
{
    if(l==r)
    {
        for(int i=1;i<=10;i++)tree[i][p]=k[l];
        return;
    }
    int mid=(l+r)>>1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
    pushup(p,l,r);
}
void update(int p,int l,int r,int x,int y)
{
    if(l==r)
    {
        for(int i=1;i<=10;i++)tree[i][p]=y;
        return;
    }
    int mid=(l+r)>>1;
    if(x<=mid)update(p<<1,l,mid,x,y);
    else update(p<<1|1,mid+1,r,x,y);
    pushup(p,l,r);
}
ll query(int p,int l,int r,int x,int y,int op)
{
    if(x<=l&&r<=y)
    {
        if(op==1)return tree[op][p];
        else return tree[op][p]*poww(op,y-r)%mod;
    }
    int mid=(l+r)>>1;
    if(op==1)
    {
        ll ans=0;
        if(x<=mid)ans=max(ans,query(p<<1,l,mid,x,y,op));
        if(mid<y)ans=max(ans,query(p<<1|1,mid+1,r,x,y,op));
        return ans;
    }
    else
    {
        ll ans=0;
        if(x<=mid)(ans+=query(p<<1,l,mid,x,y,op))%=mod;
        if(mid<y)(ans+=query(p<<1|1,mid+1,r,x,y,op))%=mod;
        return ans;
    }
}
int main()
{
    int n,q;
    scanf("%d%d",&n,&q);
    scanf("%s",s+1);
    for(int i=1;i<=n;i++)k[i]=s[i]-'0';
    build(1,1,n);
    while(q--)
    {
        int op,x,y;
        scanf("%d%d%d",&op,&x,&y);
        if(op==1)
        {
            update(1,1,n,x,y);
        }
        else
        {
            ll maxx=query(1,1,n,x,y,1);
            printf("%lld\n",query(1,1,n,x,y,maxx+1));
        }
    }
    return 0;
}

C-蓝彗星

n颗星,每颗星在天中保留t秒,si代表第i颗星的颜色,“B”代表蓝色,“R”代表红色,ai代表的i颗星出现的时间,假设黑夜无限长,问能看到蓝色彗星且看不到红色彗星的时长。(1n,t,ai100000)

思路

对于这样的题目应该一下就能想到差分,一想到差分应该就直接结束了吧……

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll mp[1000005];
char s[1000005];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    scanf("%s",s+1);
    for(int i=1;i<=n;i++)
    {
        int a;
        scanf("%d",&a);
        if(s[i]=='B')
        {
            mp[a]++;
            mp[a+m]--;
        }
        else
        {
            mp[a]-=1000000;
            mp[a+m]+=1000000;
        }
    }
    ll num=0;
    int tot=0;
    for(int i=0;i<=1000000;i++)
    {
        num+=mp[i];
        if(num>0)tot++;
    }
    printf("%d\n",tot);
    return 0;
}

D-雪色光晕

小红初始位置为(x0,y0),她每秒有一个方向向量(xi,yi),会沿着该方向跑一秒,小红想知道他在跑步过程中距离小果(x,y)的最近距离。(1n200000,109x,y,xi,yi109)

思路

这个问题就是判断线段外一点到线段的最短距离是等于到直线的距离还是到端点的距离,然后稍微运用一下高中知识就能轻松AC了。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int main()
{
    int _;
    scanf("%d",&_);
    double x0,y0,x,y;
    scanf("%lf%lf%lf%lf",&x0,&y0,&x,&y);
    double minn=1e9;
    while(_--)
    {
        double x1,y1;
        scanf("%lf%lf",&x1,&y1);
        double ans=fabs(y1*x-x1*y+x1*y0-y1*x0)/sqrt(x1*x1+y1*y1);
        if(x1*(x-x0)+y1*(y-y0)>=0&&(x1*x1+y1*y1)>=((x-x0)*(x-x0)+(y-y0)*(y-y0)-ans*ans))minn=min(minn,ans);
        else minn=min(minn,min(sqrt((x-x0)*(x-x0)+(y-y0)*(y-y0)),sqrt((x-x0-x1)*(x-x0-x1)+(y-y0-y1)*(y-y0-y1))));
        x0+=x1,y0+=y1;
    }
    printf("%.8f\n",minn);
    return 0;
}

E-真假签到题

小红拿到了一份代码:

long long f(long long x)
{
   if(x==1)return 1;
   return f(x/2)+f(x/2+x%2);
}

对于给定的x,问f(x)是多少。(1x1018)

代码

第一道签到题,但是一开始有点慌,规律找错了,哭死。所以其实就是一道输入输出题。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int main()
{
    ll n;
    scanf("%lld",&n);
    printf("%lld\n",n);
    return 0;
}

F-小红的记谱法


(1000)

思路

这题应该就是一道很简单的模拟题吧……

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
char s[1000005];
int main()
{
    scanf("%s",s+1);
    int num=0;
    int len=strlen(s+1);
    for(int i=1;i<=len;i++)
    {
        if(s[i]=='>')num++;
        else if(s[i]=='<')num--;
        else
        {
            int n=s[i]-'A'+1;
            if((n+7-2)%7==0)printf("7");
            else printf("%d",(n+7-2)%7);
            for(int i=1;i<=num;i++)printf("*");
            for(int i=num;i<=-1;i++)printf(".");
        }
    }
    return 0;
}

G-子序列权值乘积

规定一个数组的权值为最大值乘上最小值。小红有一个数组,问这个数组的所有非空子序列的权值之积对1e9+7取模后的答案是多少。(1n200000,1ai109)

思路

首先应该就能想到对这个序列排个序,然后对于一个数,他前面有a个数,后面有b个数,那么他就有2a次方的机会可以成为最大值,最小值也同理,然后差不多就能结束了,不过还要知道一个芝士点叫做欧拉降幂:

ll phi(ll n)
{
  ll rec=n;
  for(int i=2;i*i<=n;i++)
  {
      if(n%i==0)
      {
          rec=rec-rec/i;
          while(n%i==0) n/=i;
      }
  }
  if(n>1)  rec=rec-rec/n;
  return rec;
}
//一般情况下poww(a,b)%c当b很大时,就能写成
//poww(a,b%phi(mod)+phi(mod))%mod
//而对于gcd(a,mod)==1的情况,phi(mod)就变成了mod-1
//于是得到poww(a,b)%mod==poww(a,b%(mod-1))%mod
//注:对于非特殊情况,"+phi(mod)"不能省

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
ll a[1000005];
ll base[1000005];
ll poww(ll x,ll n)
{
    ll ans=1;
    while(n)
    {
        if(n&1)(ans*=x)%=mod;
        (x*=x)%=mod;
        n>>=1;
    }
    return ans;
}
int main()
{
    int n;
    scanf("%d",&n);
    base[0]=1;
    for(int i=1;i<=n;i++)base[i]=base[i-1]*2%(mod-1);
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    sort(a+1,a+n+1);
    ll ans=1;
    for(int i=1;i<=n;i++)
    {
        (ans*=poww(a[i],base[i-1]+base[n-i]))%=mod;
    }
    printf("%lld\n",ans);
}

H-真真真真真签到题

小红和小紫在一个正方体中,小紫在正中间,小红在角落,两人相距x,问正方体的体积。(1x100)

思路

第二道签到题,没什么难度吧应该……

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int main()
{
    double dis;
    scanf("%lf",&dis);
    double a=2*sqrt(dis*dis/3);
    printf("%.6f\n",a*a*a);
    return 0;
}

I-爆炸的符卡洋洋洒洒

n张符卡,每张符卡的魔力消耗为ai,威力为bi。小红要保证发出的符卡的魔力消耗之和为k的倍数,问能够发出的最大威力是多少。(1n,k1000,1ai,bi109)

思路

首先想到的当然是背包,但是1e9当然是存不下的,但是由于k的值不大,所以直接存模k后的值就能轻松解决啦。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll dp[1005][1005];
int main()
{
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
    {
        ll a,b;
        scanf("%lld%lld",&a,&b);
        for(int j=0;j<k;j++)
        {
            dp[i][a%k]=max(dp[i][a%k],b);
            if(dp[i-1][j])
            {
                dp[i][j]=max(dp[i-1][j],dp[i][j]);
                dp[i][(j+a)%k]=max(dp[i-1][j]+b,dp[i][(j+a)%k]);
            }
        }
    }
    if(dp[n][0]==0)printf("-1\n");
    else printf("%lld\n",dp[n][0]);
}

J-区间合数的最小公倍数

小红有两个整数l,r,问[l,r]中所有合数的最小公倍数是多少,答案对1e9+7取模。(1lr30000)

思路

这道题我一开始理a假了,其实就是小学的时候求最小公倍数的方法……

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1000000007;
bool prime[30005];
ll t[30005];
ll poww(ll a,ll n)
{
    ll ans=1;
    while(n)
    {
        if(n&1)ans=ans*a%mod;
        a=a*a%mod;
        n>>=1;
    }
    return ans;
}
int main()
{
    for(ll i=2;i<=30000;i++)
        for(ll j=2;j*i<=30000;j++)
            prime[i*j]=1;
    ll l,r;
    scanf("%lld%lld",&l,&r);
    ll num=0;
    for(ll i=l;i<=r;i++)
    {
        if(prime[i])
        {
            num++;
            ll n=i;
            for(ll j=2;j<=n;j++)
            {
                if(!prime[j])
                {
                    ll numm=0;
                    while(n&&n%j==0)numm++,n/=j;
                    t[j]=max(t[j],numm);
                }
            }
        }
    }
    if(num==0)printf("-1\n");
    else
    {
        ll ans=1;
        for(int i=2;i<=30000;i++)if(t[i])
        {
            ans=ans%mod*poww(i,t[i])%mod;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

K-小红的真真假假签到题题

小红拿到了一个正整数x,她想构造一个正整数y使得:1yx的倍数且y不等于x2x在二进制表示下是y在二进制表示下的一个字串且在二进制表示下1的个数不同;3y为不超过1019的正整数。(1x109)

思路

我们可以拿5举例,5的二进制表示为101,而101101能作为5y,也就是1011001=59=45,因此我们得出,y=x(1+(1<<len(x))),其中len(x)代表x的二进制表示的长度。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int main()
{
    ll x;
    scanf("%lld",&x);
    int len;
    for(int i=30;i>=0;i--)if(x&(1ll<<i)){len=i+1;break;}
    ll mul=1+(1ll<<len);
    printf("%lld\n",x*mul);
    return 0;
}

L-在这冷漠的世界里光光哭哭

有一个字符串Sq组询问,每次问在区间[l,r]中有多少个长度为3的字符串s的子序列。(1n80000,1q500000)

思路

当然能想到运用dp的思想,dp1[k][i][j]表示“k”在前k个位置的时候子序列“ikj”的个数,dp2[k][i][j]表示前k个字母中子序列“ij”的个数,dp3[k][i]表示前k个字母中子序列“i”的个数,dp4[k][i]表示后k个字母中子序列“i”的个数。然后要求区间[l,r]中子序列“ikj”的个数,我们就先二分找到区间[l,r]中第一个“k”的前一个位置kl和最后一个“k”的位置kr,就能得到dp1[kr][i][j]dp1[kl][i][j],当然这还不是答案,我们要减去一些非法的答案:(i)[kj]()()[ik](j)(i)[k](j)。然后区间[l,r]中子序列“ij”的答案也是同理。怕我讲的不清楚,特此附上solemntee大佬的题解

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
char s[80005];
ll dp1[80005][26][26];
ll tmp[26][26][26];
ll dp2[80005][26][26];
ll dp3[80005][26];
ll dp4[80005][26];
vector<int>id[26];
int main()
{
    int n,q;
    scanf("%d%d",&n,&q);
    scanf("%s",s+1);
    for(int i=0;i<26;i++)id[i].push_back(0);
    for(int i=1;i<=n;i++)
    {
        id[s[i]-'a'].push_back(i);
        for(int j=0;j<26;j++)dp3[i][j]=dp3[i-1][j];
        dp3[i][s[i]-'a']++;
        for(int j=0;j<26;j++)
        {
            for(int k=0;k<26;k++)
                dp2[i][j][k]=dp2[i-1][j][k];
            dp2[i][j][s[i]-'a']+=dp3[i-1][j];
        }
    }
    for(int i=n;i>=1;i--)
    {
        for(int j=0;j<26;j++)dp4[i][j]=dp4[i+1][j];
        dp4[i][s[i]-'a']++;
    }
    for(int i=1;i<=n;i++)
        for(int j=0;j<26;j++)
            for(int k=0;k<26;k++)
                dp1[i][j][k]=tmp[s[i]-'a'][j][k]+=dp3[i-1][j]*dp4[i+1][k];
    while(q--)
    {
        int l,r;
        char str[4];
        scanf("%d%d%s",&l,&r,str+1);
        int x=str[1]-'a',y=str[2]-'a',z=str[3]-'a';
        if(dp3[r][y]-dp3[l-1][y]==0){printf("0\n");continue;}
        int l1=lower_bound(id[y].begin(),id[y].end(),l)-id[y].begin();
        int r1=upper_bound(id[y].begin(),id[y].end(),r)-id[y].begin();
        ll ans=dp1[id[y][r1-1]][x][z]-dp1[id[y][l1-1]][x][z];//"y"在[l,r]中
        ll ans1=dp2[r][y][z]-dp2[l-1][y][z]-dp3[l-1][y]*(dp3[r][z]-dp3[l-1][z]);//"yz"在[l,r]中
        ll ans2=dp2[r][x][y]-dp2[l-1][x][y]-dp3[l-1][x]*(dp3[r][y]-dp3[l-1][y]);//"xy"在[l,r]中
        ans-=dp3[l-1][x]*ans1;//减去"x"在[1,l-1],"yz"在[l,r]的情况
        ans-=dp4[r+1][z]*ans2;//减去"xy"在[l,r],"z"在[r+1,n]的情况
        ans-=dp3[l-1][x]*(dp3[r][y]-dp3[l-1][y])*dp4[r+1][z];//减去"x"在[1,l-1],"y"在[l,r],"z"在[r+1,n]的情况
        printf("%lld\n",ans);
    }
    return 0;
}

__EOF__

本文作者Jerry-Black
本文链接https://www.cnblogs.com/Jerry-Black/p/15874684.html
关于博主:小蒟蒻一只( ̄^ ̄)ゞ
版权声明:转载请注明来源哟~ QAQ
声援博主:UP UP UP !!!
posted @   Jerry_Black  阅读(129)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示