牛客练习赛53(A,B(分块),C(bitset),E(线段树))

题目链接

A-超越学姐爱字符串

简单递推式,简单推前几项就可以得到答案了,就是与斐波拉契数有关。

也可以dp递推。

dp[i][0]表示长度为 i 且最后一个字符是‘c’的情况数,dp[i][1]
表 示 长 度 为 i 且 最 后 一 个 字 符 是 ‘ y ’ 的 情 况 数 。
dp[i+1][0]=dp[i][1],dp[i+1][1]=dp[i][0]+dp[i][1]。

#include
using namespace std;
const int N=1e5+10;
typedef long long ll;
const ll mod=1e9+7;
ll f[N];
int main()
{
    f[1]=1;
    f[2]=2;
    for(int i=3;i

B-美味果冻

这类题对于数学推导较差的很吃亏。

转换一下就是另一种分块了,分块还有另一种形式:https://blog.csdn.net/qq_41286356/article/details/94970115

下面题解参考来自:https://blog.csdn.net/oampamp1/article/details/102513734

牛客练习赛53(A,B(分块),C(bitset),E(线段树))_第1张图片

牛客练习赛53(A,B(分块),C(bitset),E(线段树))_第2张图片

/*妙*/

#include
using namespace std;
long long a[3000010];
const long long mod=1e9+7;
long long div2=500000004;
void init1(int k) {
    for(int i = 1; i <= k; i++) {
        a[i] = 1;
    }
}
void init2(int k) {
    for(int i = 1; i <= k; i++) {
        a[i] = (a[i] * i) % mod;
    }
}
long long f3(long long n)
{
    long long sum=0;
    init1(n);
    for(long long j=1;j<=n;j++)
    {
        init2(n / j);
        for(long long k=1;k<=n/j;k++)
        {
            long long l=min(n,(k+1)*j-1);
            sum=(sum+((((l-k*j+1+mod)%mod)*(k*j+l)%mod)*div2%mod)*a[k]%mod)%mod;
        }
    }
    return sum%mod;
}
int main()
{
    int n;
    cin >> n;
    cout << f3(n);
    return 0;
}

C-富豪凯匹配串

bitset的题。。

设一个最初的二进制d全为1.

当询问的串当前位置i是字符1.那么就去保存1的bitset去与运算一下。

以此类推。。。直到最后查询d还剩多少个1。

有点像闯关,每次有1就&1,有0就&0。。。直到最后是全部相等的,就能通关。

#include
using namespace std;
const int N=1e3+10;
bitsetst[N][2],t;
char s[N];
int n,m;
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
    {
        scanf("%s",s+1);
        for(int j=1;j<=m;++j)
        {
            if(s[j]=='1') st[j][1][i]=1;
            else st[j][0][i]=1;
        }
    }
    int q;
    scanf("%d",&q);
    while(q--)
    {
        scanf("%s",s+1);
        for(int i=1;i<=n;++i) t[i]=1;
        for(int i=1;i<=m;++i)
        {
            if(s[i]=='_') continue;
            if(s[i]=='1') t&=st[i][1];
            else t&=st[i][0];
        }
        printf("%d\n",t.count());
    }
}

E-老瞎眼 pk 小鲜肉

线段树解决偏序问题的经典问题了。

处理出合法的贡献区间和询问区间。

两种区间混合,按L从大到小排序。

遇到贡献区间就往线段树上更新值。遇到询问区间,就询问区间内的最小值即可。

这题我数组开小了wa了三发,居然不是提醒runtime error

#include
using namespace std;
const int N=2e6+10;
typedef long long ll;
const int inf=0x3f3f3f3f;
struct node
{
    int l,r,id;
};
int n,q;
int a,sum;
int vis[N];
int mi[4*N],ans[N];
vectorG;
bool cmp(node a,node b)
{
    if(a.l!=b.l) return a.l>b.l;
    return a.id>1;
    if(pos<=mid) up(id<<1,l,mid,pos,val);
    else up(id<<1|1,mid+1,r,pos,val);
    mi[id]=min(mi[id<<1],mi[id<<1|1]);
}
 
int qu(int id,int l,int r,int ql,int qr)
{
    if(ql<=l&&r<=qr) return mi[id];
    int mid=l+r>>1;
    int res=inf;
    if(ql<=mid)res=qu(id<<1,l,mid,ql,qr);
    if(qr>mid)res=min(res,qu(id<<1|1,mid+1,r,ql,qr));
    return res;
}
int main()
{
    scanf("%d%d",&n,&q);
    memset(vis,-1,sizeof(vis));
    int l,r;
    vis[0]=0;
    for(int i=1;i<=n;++i)
    {
        scanf("%d",&a);
        sum=sum^a;
        if(vis[sum]!=-1)
        {
            l=vis[sum]+1;
            r=i;
            G.push_back({l,r,0});
        }
        vis[sum]=i;
    }
 
    for(int i=1;i<=4*n;++i) mi[i]=inf;
 
    for(int i=1;i<=q;++i)
    {
        int l,r;
        scanf("%d%d",&l,&r);
        G.push_back({l,r,i});
    }  
    sort(G.begin(),G.end(),cmp);
 
    for(node v:G)
    {
        if(v.id!=0) ans[v.id]=qu(1,1,n,1,v.r);
        else up(1,1,n,v.r,v.r-v.l+1);
    }
 
    for(int i=1;i<=q;++i) {
        if(ans[i]==inf) printf("-1\n");
        else printf("%d\n",ans[i]);
    }
}

 

你可能感兴趣的:(牛客题解,数据结构---STL,STL--bitset)