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)

2021杭电多校第八场题解_质因数2021杭电多校第八场题解_i++_02
#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\n",((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了一发

2021杭电多校第八场题解_质因数2021杭电多校第八场题解_i++_02
#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即可

2021杭电多校第八场题解_质因数2021杭电多校第八场题解_i++_02
#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\n",ans[id[i]]);
    }
}
View Code

持续更新中……