NOI模拟20220529

T1 s1mple

容斥,先转化成没有0的限制,最后高维查分搞回来

然后发现变成了许多链,于是fwt再卷起来复杂度就对了

AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ull unsigned 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=20;
const ull bas=131;
int n,u,v,m,b[N][N];char ch[N];
int res[1<<17],ans[1<<17];
unordered_map<ull,int> mp;int hf[N];
int sm[1<<17],f[1<<17][18];
int a[18][1<<17],g[1<<17];
void fwt(int *a){
    fo(i,0,n-1)fo(s,0,u)if(s>>i&1)a[s]+=a[s^(1<<i)];
}
void ifwt(int *a){
    fo(i,0,n-1)fo(s,0,u)if(s>>i&1)a[s]-=a[s^(1<<i)];
}
signed main(){
    freopen("s1mple.in","r",stdin);
    freopen("s1mple.out","w",stdout);
    // cerr<<(sizeof(f)+sizeof(g)>>20)<<endl;
    n=read();u=(1<<n)-1;v=(1<<n-1)-1;
    fo(i,1,n){
        scanf("%s",ch+1);
        fo(j,1,n)b[i][j]=ch[j]-'0';
    }
    fo(s,0,u)sm[s]=sm[s>>1]+(s&1);
    fo(i,1,n)f[1<<i-1][i]=1;
    fo(t,1,u){
        fo(i,1,n)if(f[t][i]){
            a[sm[t]][t]+=f[t][i];
            fo(j,1,n)if(!(t>>j-1&1)&&b[i][j]){
                f[t|(1<<j-1)][j]+=f[t][i];
            }
        }
    }
    fo(i,1,17)fwt(a[i]);
    fo(s,0,v){
        memset(hf,0,sizeof(hf));int las=1;
        fo(i,1,n-1)if(!(s>>i-1&1)){
            hf[i-las+1]++;las=i+1;
        }hf[n-las+1]++;
        ull hs=0;fo(i,1,n)hs=hs*131+hf[i];
        if(mp.find(hs)!=mp.end()){
            res[s]=mp[hs];continue;
        }
        fo(i,0,u)g[i]=1;
        fo(i,1,n)fo(j,1,hf[i]){
            fo(k,0,u)g[k]*=a[i][k];
        }
        ifwt(g);res[s]=g[u];
        mp[hs]=res[s];
    }
    fo(i,0,n-2)fo(s,0,v)if(!(s>>i&1))res[s]-=res[s^(1<<i)];
    m=read();
    while(m--){
        scanf("%s",ch+1);
        int ss=0;
        fo(i,1,n-1)ss|=(ch[i]-'0')<<i-1;
        printf("%lld\n",res[ss]);
    }
}

T2 s2mple

考场上只会\(n^2logn\),沈老师有一个期望根号log的做法,但是我多次询问然而懵逼结束,现在回了

其实是个扫描线,我们把所有询问离线在后缀树节点上

正解要转化一下,变成在这个字符串前面后面添字符,最后成为子串的方案数

在前面添加就是fail树中的子树,后面就SAM上的dp

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=4e5+5;
int n,m;char s[N];
struct SAM{
    struct POT{
        int son[26],fail;
        int len;
    }tr[N*2];
    int seg,las;
    SAM(){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;
struct E{int to,nxt;}e[N*2];
int head[N*2],rp,ps[N];
void add_edg(int x,int y){e[++rp].to=y;e[rp].nxt=head[x];head[x]=rp;}
int dp[N*2],sm[N*2],bc[N],wo[N*2];
void sol(){
    fo(i,1,sam.seg)bc[sam.tr[i].len]++;
    fo(i,1,n)bc[i]+=bc[i-1];
    fo(i,1,sam.seg)wo[bc[sam.tr[i].len]--]=i;
    fu(i,sam.seg,1){
        fo(j,0,25)dp[wo[i]]+=dp[sam.tr[wo[i]].son[j]];
        dp[wo[i]]++;
    }
}
int fa[N*2][21];
void dfs(int x,int f){
    // if(x==3)cerr<<f<<endl;
    fa[x][0]=f;sm[x]=0;
    fo(i,1,20)fa[x][i]=fa[fa[x][i-1]][i-1];
    for(int i=head[x];i;i=e[i].nxt){
        int y=e[i].to;dfs(y,x);
        sm[x]+=sm[y]+(sam.tr[y].len-sam.tr[x].len)*dp[y];
    }
}
int work(int l,int r){
    int now=ps[r],ret=0,ln=r-l+1;
    // cerr<<now<<" "<<fa[now][0]<<" "<<sam.tr[fa[now][0]].len<<" "<<ln<<endl;
    fu(i,20,0)if(fa[now][i]&&sam.tr[fa[now][i]].len>=ln)now=fa[now][i];
    // cerr<<sam.tr[now].son[1]<<endl;
    // cerr<<now<<" "<<sam.tr[now].len<<" "<<sam.tr[sam.tr[now].fail].len<<" "<<ln<<endl;
    ret=sm[now]+(sam.tr[now].len-ln+1)*dp[now];
    return ret;
}
signed main(){
    freopen("s2mple.in","r",stdin);
    freopen("s2mple.out","w",stdout);
    n=read();m=read();
    scanf("%s",s+1);
    fo(i,1,n)sam.ins(s[i]-'a'),ps[i]=sam.las;
    fo(i,2,sam.seg){
        // cerr<<sam.tr[i].fail<<" "<<i<<endl;
        add_edg(sam.tr[i].fail,i);
    }
    sol();dfs(1,0);
    while(m--){
        int l=read(),r=read();
        printf("%lld\n",work(l,r));
    }
}
posted @ 2022-06-08 14:37  fengwu2005  阅读(38)  评论(0编辑  收藏  举报