• 2021杭电多校第八场题解


    C

    签到题

    从后往前搜,每次搜最短的距离,然后取最长,O(Tn^2)

    D

    就是一纸老虎题,但又因为数组开小RE,线段树开4倍开4倍!!!

    操作2本质上是将最大位前移,也就是乘2(如果是0则不动所以也是*2),操作1是砍掉一个最小位,操作3就是区间查询。

    于是可以建立两个线段树,第一棵维护最大位,只有*2(操作2)和*0(对只有最大位的数进行操作1);第二棵维护后面的位(因为砍掉一位是单点修改),也可以用树状数组维护,操作1则是暴力枚举不为0的数砍掉(因为每个数最多只会被执行30次操作1)。时间复杂度是O(nlog(a[i])logn)

    #include<bits/stdc++.h>
    #define lson l,mid,rt<<1
    #define rson mid+1,r,rt<<1|1
    using namespace std;
    const int N=1e5+7,mod=998244353;
    int n,a[N],b[N],c[N],s[N],pre[N],nxt[N],lazy[N<<2],sum[N<<2];
    void add(int x,int v){while(x<=n)c[x]+=v,x+=x&-x;}
    int ask(int x){int ret=0;while(x)ret+=c[x],x-=x&-x;return ret;}
    void adds(int x,int v){while(x<=n)s[x]=(s[x]+v)%mod,x+=x&-x;}
    int asks(int x){int ret=0;while(x)ret=(ret+s[x])%mod,x-=x&-x;return ret;}
    void build(int l,int r,int rt)
    {
        lazy[rt]=1;
        if(l==r){sum[rt]=b[l];return;}
        int mid=l+r>>1;
        build(lson),build(rson);
        sum[rt]=(sum[rt<<1]+sum[rt<<1|1])%mod;
    }
    void pushdown(int rt)
    {
        if(lazy[rt]!=1)
        {
            int c=lazy[rt];
            lazy[rt]=1;
            lazy[rt<<1]=1ll*lazy[rt<<1]*c%mod;
            lazy[rt<<1|1]=1ll*lazy[rt<<1|1]*c%mod;
            sum[rt<<1]=1ll*sum[rt<<1]*c%mod;
            sum[rt<<1|1]=1ll*sum[rt<<1|1]*c%mod;
        }
    }
    void update(int L,int R,int c,int l,int r,int rt)
    {
        if(L<=l&&r<=R){sum[rt]=1ll*sum[rt]*c%mod,lazy[rt]=1ll*lazy[rt]*c%mod;return;}
        pushdown(rt);
        int mid=l+r>>1;
        if(L<=mid)update(L,R,c,lson);
        if(R>mid)update(L,R,c,rson);
        sum[rt]=(sum[rt<<1]+sum[rt<<1|1])%mod;
    }
    int query(int L,int R,int l,int r,int rt)
    {
        if(L<=l&&r<=R)return sum[rt];
        pushdown(rt);
        int mid=l+r>>1,ret=0;
        if(L<=mid)ret=(ret+query(L,R,lson))%mod;
        if(R>mid)ret=(ret+query(L,R,rson))%mod;
        return ret;
    }
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d",&n);
            for(int i=1;i<=n;++i)c[i]=s[i]=0,pre[i]=i-1,nxt[i]=i+1;
            pre[n+1]=n,nxt[0]=1;
            for(int i=1;i<=n;++i)
            {
                scanf("%d",&a[i]);
                add(i,1);
                for(int j=29;~j;--j)if((a[i]>>j)&1)
                {
                    b[i]=(1<<j),a[i]-=(1<<j);
                    break;
                }
            }
            build(1,n,1);
            for(int i=1;i<=n;++i)if(a[i])adds(i,a[i]);
            int Q,op,l,r,ans;scanf("%d",&Q);
            while(Q--)
            {
                scanf("%d%d%d",&op,&l,&r);
                if(op==1)printf("%d
    ",((query(l,r,1,n,1)+asks(r))%mod-asks(l-1)+mod)%mod);
                else if(op==3)update(l,r,2,1,n,1);
                else{
                    int pos=r+1,L=l,R=r,mid;
                    while(L<=R)
                    {
                        mid=L+R>>1;
                        if(ask(mid)-ask(L-1))pos=mid,R=mid-1;
                        else L=mid+1;
                    }
                    while(pos<=r)
                    {
                        if(!a[pos])
                        {
                            add(pos,-1);
                            nxt[pre[pos]]=nxt[pos];
                            pre[nxt[pos]]=pre[pos];
                            update(pos,pos,0,1,n,1);
                        }
                        else{
                            adds(pos,mod-(a[pos]&-a[pos]));
                            a[pos]-=a[pos]&-a[pos];
                        }
                        pos=nxt[pos];
                    }
                }
            }
        }
    }
    View Code

    F

    签到题

    每次取gcd等价于每次砍掉至少一个因子,于是可以等价于把a[i]变成d(a[i]),即a[i]的因子数,然后看异或起来是否是0即可。

    但暴力搜质因数会超时,要把根号以内的质因数筛出来减小搜索范围。

    zz的我把a数组也开成了质数大小(即4000)导致RE了一发

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e6+7;
    int n,cnt,pri[N],a[N];
    int main()
    {
        for(int i=2;i<=4000;++i)
        {
            int flag=1;
            for(int j=2;j*j<=i;++j)if(i%j==0){flag=0;break;}
            if(flag)pri[++cnt]=i;
        }
        int T;scanf("%d",&T);
        while(T--)
        {
            scanf("%d",&n);
            for(int i=1,x;i<=n;++i)
            {
                a[i]=0;
                scanf("%d",&x);
                for(int j=1;pri[j]*pri[j]<=x;++j)
                if(x%pri[j]==0)
                {
                    while(x%pri[j]==0)x/=pri[j],++a[i];
                }
                if(x>1)++a[i];
            }
            int sum=0;
            for(int i=1;i<=n;++i)sum^=a[i];
            if(!sum)puts("Bob");else puts("Alice");
        }
    }
    View Code

    I

    这种统计出现多少次的自然想到AC自动机,于是就是个模板题。每次记录下串长和上次出现的位置,加起来不超过i即可

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e5+7,M=3e6+7;
    int n,cnt,id[N],ch[M][26],val[M],fail[M],lst[M],len[M],ans[M];
    char s[N],buf[40];
    int newnode(int l)
    {
        int x=++cnt;
        memset(ch[x],0,sizeof(ch[x]));
        len[x]=l;
        val[x]=fail[x]=lst[x]=ans[x]=0;
        return x;
    }
    int insert()
    {
        int u=1,n=strlen(buf+1);
        for(int i=1;i<=n;i++)
        {
            if(!ch[u][buf[i]-'a'])ch[u][buf[i]-'a']=newnode(i);
            u=ch[u][buf[i]-'a'];
        }
        ++val[u],lst[u]=-1;
        return u;
    }
    void build()
    {
        queue<int>q;q.push(1);
        while(!q.empty())
        {
            int u=q.front();q.pop();
            for(int i=0;i<26;i++)
            if(ch[u][i])
            {
                int v=fail[u];
                while(v&&!ch[v][i])v=fail[v];
                fail[ch[u][i]]=v?ch[v][i]:1;
                q.push(ch[u][i]);
            }
        }
    }
    void query()
    {
        int u=1,n=strlen(s+1);
        for(int i=1;i<=n;i++)
        {
            while(u&&!ch[u][s[i]-'a'])u=fail[u];
            u=u?ch[u][s[i]-'a']:1;
            int x=u;
            do{
                if(val[x]&&(lst[x]==-1||lst[x]+len[x]<=i))lst[x]=i,++ans[x];
                x=fail[x];
            }while(x);
        }
    }
    int main()
    {
        int T;scanf("%d",&T);
        while(T--)
        {
            cnt=0,newnode(0);
            scanf("%s%d",s+1,&n);
            for(int i=1;i<=n;i++)scanf("%s",buf+1),id[i]=insert();
            build();
            query();
            for(int i=1;i<=n;i++)printf("%d
    ",ans[id[i]]);
        }
    }
    View Code

    持续更新中……

  • 相关阅读:
    python基础教程学习笔记十五
    使用win32imageinstall安装ubuntu
    python基础教程学习笔记十四
    python基础教程学习笔记十二
    ztpto图片轮播
    jQuery 获取屏幕高度、宽度
    h5新属性,可编辑的段落 contenteditable=“true”
    奋斗的奴隶博客中的牛逼js小测试
    ps4将图片弄成透明图片
    图片轮播,纯js+css
  • 原文地址:https://www.cnblogs.com/hfctf0210/p/15139236.html
Copyright © 2020-2023  润新知