NOI模拟21

今天是辽宁OI的省选题,似乎没有那么难,但是T1总感觉在哪里见过,于是就死磕了...

第一题就发现一个性质后就非常简单,否则只能做背包,但是做背包的过程中仍然用到这个性质呵呵

第二题就傻逼dp,7维

第三题考场上用了4分钟连看题加上打出来,拿到了40pts

第四题考场上没有看,但是似乎是构造

T3 盒

组合意义神题,建议看其他人的题解,我不想写式子...

有一个东西要记录一下,从(0,0)走到(n,m)的方案数,一种是直接组合数

另一种是枚举某一行在哪里走到下一行的(可以是任意一行),枚举一下列,然后前面组合数乘上后面组合数就行

通过组合意义的转化可以O(1)去跳,于是均摊就是O(n)的复杂度了

调了好久...

AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
    int s=0,t=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
    while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
    return s*t;
}
const int N=4e6+5;
const int mod=998244353;
int ksm(int x,int y){
    int ret=1;
    while(y){
        if(y&1)ret=ret*x%mod;
        x=x*x%mod;y>>=1;
    }return ret;
}
int T,R=4e6,n,ans,a[N],s[N],w[N];
int jc[N],inv[N];
int C(int x,int y){
    if(x<y)return 0;
    return jc[x]*inv[y]%mod*inv[x-y]%mod;
}
struct node{
    int n,m,i,k,res;
    void movi(int x){
        while(i<x){
            res=(res-C(i+k,i)*C(n-i-1+m-k-1,n-i-1)%mod+mod)%mod;
            i++;
        }
    }
    void movk(int x){
        while(k<x){
            k++;
            res=(res+C(i+k-1,k)*C(m-k+n-i-1,m-k))%mod;
        }
    }
}f1,f2;
signed main(){
    T=read();
    jc[0]=1;fo(i,1,R)jc[i]=jc[i-1]*i%mod;
    inv[0]=1;inv[R]=ksm(jc[R],mod-2);
    fu(i,R-1,1)inv[i]=inv[i+1]*(i+1)%mod;
    while(T--){
        n=read();ans=0;
        fo(i,1,n)a[i]=read();
        fo(i,1,n-1)w[i]=read();
        fo(i,1,n)s[i]=s[i-1]+a[i];
        f1=node{n,s[n],0,0,C(s[n]+n-1,s[n]-0)};
        f2=node{n+1,s[n]-1,0,0,C(s[n]+n-1,s[n]-1)};
        fo(i,1,n-1){
            int res=0;
            res=(res+i*C(s[n]+n-1,n)%mod-s[i]*C(s[n]+n-1,s[n])%mod+mod)%mod;
            // cerr<<res<<" "<<i<<" "<<s[i]<<" "<<i*C(s[n]+n-1,n)%mod-s[i]*C(s[n]+n-1,s[n])%mod<<endl;
            if(s[i])f1.movk(s[i]),f1.movi(i),res=(res+2*s[i]*f1.res)%mod;
            // cerr<<f1.res<<" ";
            if(s[i])f2.movk(s[i]-1),f2.movi(i+1),res=(res-2*i*f2.res%mod+mod)%mod;
            // cerr<<f2.res<<" "<<ans<<endl;
            ans=(ans+res*w[i])%mod;
        }
        printf("%lld\n",ans);
    }
}

T4 串

构造好题,可以发现下界是\(\frac{n}{2}\),从后面一半开始走,每次向前挪动

发现此时的限制是不能把长度缩成0,于是如果可以找到两个相同的串,那么我们就可以循环跳回去,于是可以长度变成0

那么我们就直接对所有的出现两次以上的串进行统计就好了,最后和下界取max

AC_code
#include<bits/stdc++.h>
using namespace std;
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
    int s=0,t=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
    while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
    return s*t;
}
const int N=5e5+5;
int T,n,ans;char s[N];
int siz[N*2],lft[N*2],buc[N],who[N*2];
struct SAM{
    struct POT{
        int son[26],fail;
        int len;
    }tr[N*2];
    int seg,las;
    SAM(){seg=las=1;}
    void clear(){
        fo(i,1,seg){
            siz[i]=0;lft[i]=0;
            fo(j,0,25)tr[i].son[j]=0;
        }seg=las=1;
    }
    void ins(int c){
        int np=++seg,p=las;las=np;
        tr[np].len=tr[p].len+1;
        while(p&&!tr[p].son[c])tr[p].son[c]=np,p=tr[p].fail;
        if(!p)tr[np].fail=1;
        else {
            int q=tr[p].son[c];
            if(tr[q].len==tr[p].len+1)tr[np].fail=q;
            else {
                int nq=++seg;
                tr[nq]=tr[q];tr[nq].len=tr[p].len+1;
                tr[np].fail=tr[q].fail=nq;
                while(p&&tr[p].son[c]==q)tr[p].son[c]=nq,p=tr[p].fail;
            }
        }
    }
}sam;
void sol(){
    scanf("%s",s+1);n=strlen(s+1);ans=0;sam.clear();
    fo(i,1,n)sam.ins(s[i]-'a'),siz[sam.las]=1,lft[sam.las]=i;
    fo(i,1,sam.seg)buc[sam.tr[i].len]++;
    fo(i,1,n)buc[i]+=buc[i-1];
    fo(i,1,sam.seg)who[buc[sam.tr[i].len]--]=i;
    fo(i,1,n)buc[i]=0;
    fu(i,sam.seg,2){
        int x=who[i];
        siz[sam.tr[x].fail]+=siz[x];
        if(!lft[sam.tr[x].fail]||lft[sam.tr[x].fail]>lft[x])lft[sam.tr[x].fail]=lft[x];
    }
    fo(i,1,sam.seg)if(siz[i]>1){
        // cerr<<lft[i]<<" "<<sam.tr[i].len<<endl;
        ans=max(ans,(n-lft[i]>>1)+sam.tr[i].len);
    }ans=max(ans,n>>1);
    printf("%d\n",ans);
}
signed main(){
    T=read();
    while(T--)sol();
    return 0;
}
posted @ 2022-06-08 15:01  fengwu2005  阅读(53)  评论(1编辑  收藏  举报