冲刺国赛模拟 4

牛子老师认为 xmz 回来了他终于有打的原因了,这就是神吗!

T1 挂了 45,又垫底了!

突然发现 tlecoders 和题库考试都叫冲刺国赛模拟。那到时候 url 好像会重。

宝石

首先套路拆成每个颜色的贡献。然后对于每个颜色分开考虑,用全部方案减掉没有这个颜色的方案。这个容易统计,可以对于每个不是这个颜色的连通块在最高的位置统计答案,线段树合并维护一下连通块大小就完事。

挂成狗,我只能说好似。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#include <vector>
using namespace std;
const int mod=998244353;
struct gra{
    int v,next;
}edge[1000010];
int tot,head[500010];
void add(int u,int v){
    edge[++tot].v=v;edge[tot].next=head[u];head[u]=tot;
}
int n,m,k,jc[500010],inv[500010],w[500010];
int A(int n,int m){
    if(n<m)return 0;
    return 1ll*jc[n]*inv[n-m]%mod;
}
struct node{
    #define lson tree[rt].ls
    #define rson tree[rt].rs
    int ls,rs,sum;
}tree[500010<<6];
int t,rt[500010];
void update(int &rt,int L,int R,int pos,int val){
    if(!rt)rt=++t;
    if(L==R){
        tree[rt].sum=val;return;
    }
    int mid=(L+R)>>1;
    if(pos<=mid)update(lson,L,mid,pos,val);
    else update(rson,mid+1,R,pos,val);
}
int query(int rt,int L,int R,int pos){
    if(!rt)return 0;
    if(L==R)return tree[rt].sum;
    int mid=(L+R)>>1;
    if(pos<=mid)return query(lson,L,mid,pos);
    else return query(rson,mid+1,R,pos);
}
int merge(int x,int y,int l,int r){
    if(!x||!y)return x|y;
    if(l==r){
        tree[x].sum+=tree[y].sum;return x;
    }
    int mid=(l+r)>>1;
    tree[x].ls=merge(tree[x].ls,tree[y].ls,l,mid);
    tree[x].rs=merge(tree[x].rs,tree[y].rs,mid+1,r);
    return x;
}
int size[500010],ans;
void dfs(int x,int f){
    size[x]=1;
    for(int i=head[x];i;i=edge[i].next){
        if(edge[i].v!=f){
            dfs(edge[i].v,x);
            size[x]+=size[edge[i].v];
            rt[x]=merge(rt[x],rt[edge[i].v],1,m);
        }
    }
    update(rt[x],1,m,w[x],size[x]);
    if(f){
        int s=size[x]-query(rt[x],1,m,w[f]);
        ans=(ans-A(s,k)+mod)%mod;
    }
    else{
        for(int i=1;i<=m;i++){
            int s=size[x]-query(rt[x],1,m,i);
            ans=(ans-A(s,k)+mod)%mod;
        }
    }
}
int main(){
    scanf("%d%d%d",&n,&m,&k);jc[0]=inv[0]=inv[1]=1;
    for(int i=2;i<=n;i++)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    for(int i=1;i<=n;i++)jc[i]=1ll*jc[i-1]*i%mod,inv[i]=1ll*inv[i-1]*inv[i]%mod;
    for(int i=1;i<=n;i++)scanf("%d",&w[i]);
    for(int i=1;i<n;i++){
        int u,v;scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }
    ans=1ll*m*A(n,k)%mod;
    dfs(1,0);
    printf("%d\n",ans);
    return 0;
}

序列

原题:LOJ Round #9 CommonAnts 的调和数。

很强!这一套的 T4 是典题,q-binomial 的 lucas 定理。然而我没补 LOJ Round,估计也没时间补了。但是题面挂了导致我全程以为这玩意不可做。很乐!

首先显然有狄利克雷前缀和做法:直接做一遍狄利克雷前缀和,转移的时候乘个对应位置的质数,然后再后缀和一次,同样乘对应位置的质数,然后直接找对应位置的答案。

\(\text{lcm}=z\),考虑到只有 \(10\) 个质因子,因此暴力把它们扫下来然后 dfs 出所有能拼出来的数,对它们做狄利克雷前缀和然后在 \(x\) 计算以 \(x\)\(\gcd\) 的所有数的贡献,乘上一个贡献函数 \(f(\dfrac nx)\),其中

\[f(n)=\sum_{i=1}^ni^2[\gcd(i,z)=1] \]

这个可以暴力容斥也可以冲一发洲阁筛,效率相近。

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <unordered_map>
#define int long long
using namespace std;
const int mod=998244353,inv6=166374059;
int n,m,q,sq,cnt,M,p[20],a[200010];
int pos[200010],x[200010],k[200010];
unordered_map<int,int>sum,mp;
void ins(int x){
    for(int i=1;i<=cnt;i++)while(x%p[i]==0)x/=p[i];
    for(int i=2;i*i<=x;i++){
        if(x%i==0){
            p[++cnt]=i;
            while(x%i==0)x/=i;
        }
    }
    if(x!=1)p[++cnt]=x;
}
void dfs(int x,int val){
    a[++a[0]]=val;
    for(int i=x;i<=cnt&&1ll*p[i]*val<=n;i++)dfs(i,val*p[i]);
}
int g1[200010],g2[200010];
int find(int x){
    return x<=sq?g1[x]:g2[n/x];
}
int s(int n){
    n%=mod;
    return 1ll*n*(n+1)%mod*(2*n+1)%mod*inv6%mod;
}
signed main(){
    scanf("%lld%lld%lld",&n,&m,&q);sq=sqrt(n);
    for(int i=1;i<=m;i++){
        scanf("%lld%lld",&pos[i],&x[i]);sum[pos[i]]=(sum[pos[i]]+x[i])%mod;
        ins(pos[i]);
    }
    for(int i=1;i<=q;i++)scanf("%lld",&k[i]),ins(k[i]);
    sort(p+1,p+cnt+1);
    dfs(1,1);
    sort(a+1,a+a[0]+1);
    int blo=n/(sq+1);
    for(int i=1;i<=sq;i++)g1[i]=s(i);
    for(int i=1;i<=blo;i++)g2[i]=s(n/i);
    for(int i=1;i<=cnt;i++){
        int t=1ll*p[i]*p[i]%mod,lim=blo/p[i];
        for(int j=1;j<=lim;j++)g2[j]=(g2[j]-t*g2[j*p[i]]%mod+mod)%mod;
        for(int j=lim+1;j<=blo;j++)g2[j]=(g2[j]-t*g1[n/(j*p[i])]%mod+mod)%mod;
        for(int j=sq;j>=1;j--)g1[j]=(g1[j]-t*g1[j/p[i]]%mod+mod)%mod;
    }
    for(int i=1;i<=cnt;i++){
        for(int j=1;j<=a[0];j++){
            if(a[j]%p[i]==0){
                sum[a[j]]=(sum[a[j]]+1ll*sum[a[j]/p[i]]*p[i])%mod;
            }
        }
    }
    for(int i=1;i<=a[0];i++)sum[a[i]]=1ll*sum[a[i]]*find(n/a[i])%mod;
    for(int i=1;i<=cnt;i++){
        for(int j=a[0];j>=1;j--){
            if(a[j]%p[i]==0){
                sum[a[j]/p[i]]=(sum[a[j]/p[i]]+1ll*sum[a[j]]*p[i])%mod;
            }
        }
    }
    for(int i=1;i<=q;i++)printf("%lld\n",sum[k[i]]);
    return 0;
}

制胡窜

原题:CF1043G。*3500。

首先显然答案 \(-1,1,2,3,4\)。一个一个判。

\(-1\) 比较简单,爆扫一遍判没有字符相等就是 \(-1\)

\(1\) 也好,暴力枚举因子,如果有整周期就是 \(1\)

然后是 \(2\)。有三种情况是 \(2\)\(aab,baa,aba\)。前两种是相同的,可以用优秀的拆分那题的方法预处理每个位置开头 / 结尾的形如 \(aa\) 的串的最短长度,每次更新一段因此可以用并查集维护。

第三个需要判有无 border。可以用 border 的四种求法的方法,但是有更简单的方法。

根号分治,小于根号长度爆扫。大于根号长度的可以发现最短 border 在 \(sa\) 数组上到左端点 \(l\) 的距离小于根号,因为它在原串中位置不交。于是在 rk 上扫就行了。

然后是 \(3\)\(3\) 的情况是 \(abac,baca,baac\)。第一个和第二个直接预处理每个字符的前后出现位置判断。第三个可以用之前的并查集挂上个 st 表查最小值。

如果以上都不是那就是 \(4\) 了。

代码体积有点大。

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <cmath>
using namespace std;
int n,sq,m;
char s[200010];
struct SA{
    int sa[200010],rk[200010],cnt[200010],rk2[200010],id[200010],key[200010];
    int height[200010],st[200010][20];
    void getsa(){
        int m=127;
        for(int i=1;i<=n;i++){
            rk[i]=s[i];cnt[rk[i]]++;
        }
        for(int i=1;i<=m;i++)cnt[i]+=cnt[i-1];
        for(int i=n;i>=1;i--)sa[cnt[rk[i]]--]=i;
        for(int w=1;;w<<=1){
            int p=0;
            for(int i=n;i>n-w;i--)id[++p]=i;
            for(int i=1;i<=n;i++)if(sa[i]>w)id[++p]=sa[i]-w;
            for(int i=1;i<=m;i++)cnt[i]=0;
            for(int i=1;i<=n;i++)key[i]=rk[id[i]],cnt[key[i]]++;
            for(int i=1;i<=m;i++)cnt[i]+=cnt[i-1];
            for(int i=n;i>=1;i--)sa[cnt[key[i]]--]=id[i];
            for(int i=1;i<=n;i++)rk2[i]=rk[i];
            p=0;
            for(int i=1;i<=n;i++){
                if(rk2[sa[i]]==rk2[sa[i-1]]&&rk2[sa[i]+w]==rk2[sa[i-1]+w])rk[sa[i]]=p;
                else rk[sa[i]]=++p;
            }
            if(p==n){
                for(int i=1;i<=n;i++)sa[rk[i]]=i;break;
            }
            m=p;
        }
        for(int i=1,k=0;i<=n;i++){
            if(k)k--;
            while(s[i+k]==s[sa[rk[i]-1]+k])k++;
            height[rk[i]]=k;
        }
        for(int i=1;i<=n;i++)st[i][0]=height[i];
        for(int j=1;j<=__lg(n);j++){
            for(int i=1;i+(1<<j)-1<=n;i++){
                st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]);
            }
        }
    }
    int query(int x,int y){
        x=rk[x];y=rk[y];
        if(x>y)swap(x,y);
        x++;
        int k=__lg(y-x+1);
        return min(st[x][k],st[y-(1<<k)+1][k]);
    }
}sa1,sa2;
int lcp(int x,int y){
    return sa1.query(x,y);
}
int lcs(int x,int y){
    return sa2.query(n-x+1,n-y+1);
}
struct Uni{
    int fa[200010],val[200010];
    int find(int x){
        return x==fa[x]?fa[x]:fa[x]=find(fa[x]);
    }
    void merge(int l,int r,int v){
        if(l>r)return;
        for(int i=find(l);i<=r;i=find(i+1)){
            val[i]=v;fa[i]=i+1;
        }
    }
}L,R;
struct ST{
    int st[200010][20];
    void build(){
        for(int i=1;i<=n;i++)st[i][0]=i+L.val[i]-1;
        for(int j=1;j<=__lg(n);j++){
            for(int i=1;i+(1<<j)-1<=n;i++){
                st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]);
            }
        }
    }
    int query(int l,int r){
        int k=__lg(r-l+1);
        return min(st[l][k],st[r-(1<<k)+1][k]);
    }
}st;
int pre[200010],nxt[200010],pos[26];
int solve(int l,int r){
    int len=r-l+1;
    bool jud=false;
    int cnt[26];
    for(int i=0;i<26;i++)cnt[i]=0;
    for(int i=l;i<=r;i++){
        if(cnt[s[i]-'a']){
            jud=true;break;
        }
        cnt[s[i]-'a']++;
    }
    if(!jud)return -1;
    for(int i=1;i*i<=len;i++){
        if(len%i==0){
            int d=i;
            if(lcp(l,l+d)>=len-d)return 1;
            if(i!=1){
                d=len/i;
                if(lcp(l,l+d)>=len-d)return 1;
            }
        }
    }
    if(l+L.val[l]-1<=r||r-R.val[r]+1>=l)return 2;
    for(int i=1;i<=min(sq,len-1);i++)if(lcp(l,r-i+1)>=i)return 2;
    int lc=n;
    for(int i=sa1.rk[l]-1;i>=1;i--){
        int pos=sa1.sa[i];
        if(sa1.st[i+1][0]<sq)break;
        lc=min(lc,sa1.st[i+1][0]);
        if(pos>l&&pos<=r&&pos+lc-1>=r)return 2;
    }
    lc=n;
    for(int i=sa1.rk[l]+1;i<=n;i++){
        int pos=sa1.sa[i];
        if(sa1.st[i][0]<sq)break;
        lc=min(lc,sa1.st[i][0]);
        if(pos>l&&pos<=r&&pos+lc-1>=r)return 2;
    }
    if(nxt[l]<=r||pre[r]>=l||st.query(l,r)<=r)return 3;
    return 4;
} 
int main(){
    scanf("%d%s%d",&n,s+1,&m);sq=sqrt(n);
    for(int i=1;i<=n;i++)pre[i]=pos[s[i]-'a'],pos[s[i]-'a']=i;
    for(int i=0;i<26;i++)pos[i]=n+1;
    for(int i=n;i>=1;i--)nxt[i]=pos[s[i]-'a'],pos[s[i]-'a']=i;
    sa1.getsa();reverse(s+1,s+n+1);sa2.getsa();reverse(s+1,s+n+1);
    for(int i=1;i<=n+1;i++)L.fa[i]=R.fa[i]=i,L.val[i]=R.val[i]=0x3f3f3f3f;
    for(int i=1;i<=(n>>1);i++){
        for(int j=i;j+i<=n;j+=i){
            int x=j,y=j+i;
            int l=lcs(x,y),r=lcp(x,y);
            x=x-l+1,y=y+r-1;
            L.merge(x,y-2*i+1,i*2);R.merge(x+2*i-1,y,i*2);
        }
    }
    st.build();
    while(m--){
        int l,r;scanf("%d%d",&l,&r);
        printf("%d\n",solve(l,r));
    }
    return 0;
}
posted @ 2023-05-24 17:59  gtm1514  阅读(18)  评论(0编辑  收藏  举报