【考试总结】2022-03-17

传教

先将两个序列差分之后异或起来,那么取模 k 相同的数字分到一组里面

这时每次进行的修改给一个组就是后缀异或一个数字,查询序列里面为 0 的数的个数

根号分治,组内元素少的时候直接跑暴力,否则对每个组分块

由于值域非常小,那么可以给每个块开一个值域大小的桶来记录哪些数字出现过来避免带 log

Code Display
const int N=2e5+10;
int A[N],B[N],a[N],b[N],n,Q,k,tmp[N],c[N];
int rem,ans;
inline void recalc(int st,int v){
    while(st<=n-k+1){
        if(c[st]&&c[st]==v) --ans;
        if(v&&!c[st]) ++ans;
        c[st]^=v;
        st+=k;
    }
    if(st>n) return ;
    if(c[st]&&c[st]==v) --rem;
    if(v&&!c[st]) ++rem;
    c[st]^=v;
    return ;
}
struct Modular{
    int idx,n,block;
    vector<int> bel,tag,seq;
    vector<vector<int> >ton;
    inline void build(){
        n=seq.size(); block=300; bel.resize(n);
        for(int i=0;i<n;++i) bel[i]=i/block;
        ton.resize(bel[n-1]+1);
        tag.resize(bel[n-1]+1);
        for(int i=0;i<=bel[n-1];++i) ton[i].resize(1<<15);
        for(int i=0;i<n;++i) ton[bel[i]][seq[i]]++;
    }
    inline int get_val(int x){return tag[bel[x]]^seq[x];}
    inline void upd(int st,int val){
        if(st>=seq.size()||!val) return ;
        // from st to n-1;
        int pre=get_val(n-1);
        for(int i=bel[st]+1;i<=bel[n-1];++i){
            ans+=ton[i][tag[i]];
            tag[i]^=val;
            ans-=ton[i][tag[i]];
        }
        int now=bel[st],end=min(block*(now+1),n)-1,start=block*now;
        ans+=ton[now][tag[now]];
        for(int i=start;i<=end;++i) ton[bel[i]][seq[i]]--,seq[i]^=tag[bel[i]];
        if(rand()%10==0) rep(i,0,(1<<15)-1) assert(ton[now][i]==0);
        for(int i=st;i<=end;++i) seq[i]^=val;
        for(int i=start;i<=end;++i) ton[bel[i]][seq[i]]++;
        ans-=ton[now][tag[now]=0];
        int cur=get_val(n-1);
        if(idx!=1){
            if(!cur&&pre) --rem;
            if(cur&&!pre) ++rem;
        }
        return ;
    }
}seq[260];
int id[N],ord[N],ton[N];
signed main(){
    freopen("mission.in","r",stdin); freopen("mission.out","w",stdout);
    n=read(); k=read(); Q=read();
    for(int i=1;i<=n;++i) A[i]=read(); Down(i,n,1) a[i]=A[i]^A[i-1];
    for(int i=1;i<=n;++i) B[i]=read(); Down(i,n,1) b[i]=B[i]^B[i-1];
    rep(i,1,n) c[i]=a[i]^b[i];
    for(int i=1;i<=n-k+1;++i) if(c[i]){
        c[i+k]^=c[i];
        ++ans;
    }
    rep(i,n-k+2,n) if(c[i]) ans+=n/k>800&&n>5000,rem++;
    if(rem) print(-1); else print(ans);
    if(n/k>800&&n>5000){
        for(int i=1;i<=n;++i){
            id[i]=(i-1)%k+1;
            ord[i]=ton[id[i]]++;
            seq[id[i]].seq.emplace_back(c[i]);
        }
        for(int i=1;i<=k;++i) seq[i].build(),seq[i].idx=i;
    }
    while(Q--){
        char alp=Getalpha();
        int x=read(),y=read();
        int new_v=y;
        if(alp=='a') new_v^=A[x],A[x]=y;
        else new_v^=B[x],B[x]=y;
        if(n/k<=800||n<=5000){
            recalc(x,new_v);
            recalc(x+1,new_v);
        }else{
            seq[id[x]].upd(ord[x],new_v);
            seq[id[x+1]].upd(ord[x+1],new_v);
        }
        if(rem) print(-1); else print(ans);
    }
    return 0;
}


方程

将原式进行一定程度上的和式变换就能得到要求的是满足下式的有序三元组 (a,b,c) 的个数

a,b,cStT,(a+bc)2+ca+b(t+1)modp

关于 a,b,c 的部分是一个 x2+1x 的形式求出来关于 x 的方案数就能计算了

除法对应的方案是原根指数意义上的加减,那么进行两次卷积即可

Code Display
const int N=6e5+10;
int p,n,m,ans;
inline int inv(int x){x%=p; return ksm(x,p-2,p);}
int r[N],W[N];
inline void NTT(vector<int> &f,int lim,int opt){
    f.resize(lim);
    for(int i=0;i<lim;++i){
        r[i]=r[i>>1]>>1|((i&1)?(lim>>1):0);
        if(i<r[i]) swap(f[i],f[r[i]]);
    }
    for(int p=2;p<=lim;p<<=1){
        int len=p>>1; W[0]=1; W[1]=ksm(3,(mod-1)/p);
        if(opt==-1) W[1]=ksm(W[1],mod-2);
        for(int j=2;j<len;++j) W[j]=mul(W[j-1],W[1]);
        for(int k=0;k<lim;k+=p){
            for(int l=k;l<k+len;++l){
                int tt=mul(f[l+len],W[l-k]);
                f[l+len]=del(f[l],tt);
                ckadd(f[l],tt);
            }
        }
    }
    if(opt==-1) for(int i=0,tmp=ksm(lim,mod-2);i<lim;++i) ckmul(f[i],tmp);
    return ;
}
bool T[N];
inline int get_root(int p){
    vector<int>Div;
    int x=p-1;
    for(int i=2;i<=x;++i) if(x%i==0){
        int tim=0;
        while(x%i==0) x/=i,++tim;
        Div.emplace_back(i);
    }
    for(int ans=2;;++ans){
        for(auto t:Div) if(ksm(ans,(p-1)/t,p)==1) goto Fail;
        return ans;
        Fail:;
    }   
}
int id[N],val[N];
#define poly vector<int> 
inline poly relabel(vector<int> F){
    vector<int> ans(p);
    for(int i=1;i<p;++i) ans[id[i]]=F[i];
    return ans;
}
signed main(){
    freopen("equation.in","r",stdin); freopen("equation.out","w",stdout);
    p=read(); n=read(); m=read();
    int g=get_root(p),pw=1;
    for(int i=0;i<p-1;++i) id[pw]=i,val[i]=pw,pw=pw*g%p;
    vector<int> S(n),F(p),G(p);
    set<int> app;
    for(int i=0;i<n;++i) F[S[i]=read()]=1;
    for(int i=0;i<m;++i) T[p-1-read()]=1;
    int lim=1; while(lim<=(p+p)) lim<<=1;
    NTT(F,lim,1); rep(i,0,lim-1) ckmul(F[i],F[i],mod); NTT(F,lim,-1);
    rep(i,p+1,p*2) ckadd(F[i-p],F[i]),F[i]=0; F.resize(p);
    rep(i,0,n-1) G[ksm(S[i],p-2,p)]=1;
    F=relabel(F); G=relabel(G);
    NTT(F,lim,1); NTT(G,lim,1);
    rep(i,0,lim-1) ckmul(F[i],G[i]);
    NTT(F,lim,-1);
    for(int i=p-1;i<lim;++i) ckadd(F[i%(p-1)],F[i]);
    for(int i=0;i<p-1;++i) if(F[i]){
        int d=val[i],lhs=(d*d+ksm(d,p-2,p))%p;
        if(T[lhs]) ckadd(ans,F[i]);
    }
    print(ans);
    return 0;
}

移花接木

考虑求最大深度可以转化成直径的距离,注意本题中 m 非常小,那么可以递归来计算两个点之间的距离

D(i,s,t) 表示 (s,t) 在第 i 棵树上的距离,如果 min(s,t)>sizai 或者 max(s,t)sizbi 就是子问题,否则使用 xi,yi 来另算即可

在递归的过程中只会涉及到 D(j,s,u[i]/v[i]) 形式的计算,所以复杂度是 Θ(m)

选出一条直径 (s0,s1),分长度的奇偶性讨论

对于奇数长度的情况,将点重标号为 1l+1 之后发现 [1,l+12] 的点必然走向 l+1,长度是 l+1 为树根时,l+12 的子树里面每个点深度和

F(i,s,t,d) 表示 s 为根时从 std 步对应的点 子树里面的点 的深度和

分两个点在断开 (xi,yi) 之后属于几个联通块分开讨论即可

需要注意的 case 是在同一个子树里面时如果 ss,t,ui/vi 三个点两两路径的交点的距离和 d 的大小关系讨论是不是计算另一个子树内的点的贡献

在计算的过程中,注意到 F(i,s,t,0)=F(i,s,s,0),那么 s,t 的可能对数是 Θ(m) 的(直径上的点和 ui,vi),复杂度是 Θ(m3)

对于直径长度是偶数的情况,需要另外计算 G(i,s,t,d) 表示子树里面有几个点来辅助计算中间一条边的经过次数,计算方式和 F 一致,甚至可以复制之后简单改改

Code Display
const int N=200;
int a[N],b[N],x[N],y[N],m,siz[N];
pair<int,int> dia[N];
int len[N];
map<pair<int,int>,int> d[N];
inline int D(int i,int x,int y){
    if(x==y) return 0; if(x>y) swap(x,y);
    if(d[i].count({x,y})) return d[i][{x,y}];
    if(siz[a[i]]>=y) return d[i][{x,y}]=D(a[i],x,y);
    if(siz[a[i]]<x) return d[i][{x,y}]=D(b[i],x-siz[a[i]],y-siz[a[i]]);
    return d[i][{x,y}]=D(a[i],x,::x[i])+D(b[i],y-siz[a[i]],::y[i])+1;
}
map<tuple<int,int,int>,int>f[N];
inline int F(int i,int s,int t,int d){
    if(i==0) return 0;
    if(!d&&s!=t) return F(i,s,s,d);
    if(f[i].count({s,t,d})) return f[i][{s,t,d}];
    int ans=0;
    if(d==0){
        if(s>siz[a[i]]){
            s-=siz[a[i]];
            int dis=(1+D(b[i],s,y[i]))%mod*(siz[a[i]]%mod)%mod;
            ans=dis+F(b[i],s,s,0)+F(a[i],x[i],x[i],0);
            s+=siz[a[i]];
        }else{
            int dis=(D(a[i],s,x[i])+1)%mod*(siz[b[i]]%mod)%mod;
            ans=dis+F(b[i],y[i],y[i],0)+F(a[i],s,s,0);
        }
    }
    else{   
        if(s>siz[a[i]]&&t<=siz[a[i]]){
            int db=D(i,s,x[i]);
            if(d>=db) ans=F(a[i],x[i],t,d-db);
            else ans=(db-d)%mod*(siz[a[i]]%mod)%mod+F(b[i],s-siz[a[i]],y[i],d)+F(a[i],x[i],x[i],0);
        }
        else if(s<=siz[a[i]]&&t>siz[a[i]]){
            int da=D(i,s,y[i]+siz[a[i]]);
            if(d>=da) ans=F(b[i],y[i],t-siz[a[i]],d-da);
            else ans=(da-d)%mod*(siz[b[i]]%mod)%mod+F(a[i],s,x[i],d)+F(b[i],y[i],y[i],0);
        }
        else if(s>siz[a[i]]&&t>siz[a[i]]){
            s-=siz[a[i]]; t-=siz[a[i]];
            int tmp=D(b[i],s,y[i]);
            int fork=(tmp+D(b[i],s,t)-D(b[i],t,y[i]))/2;
            ans=F(b[i],s,t,d);
            if(fork>=d) ans+=(tmp-d+1)%mod*(siz[a[i]]%mod)%mod+F(a[i],x[i],x[i],0)%mod;
            s+=siz[a[i]]; t+=siz[a[i]];
        }
        else{
            int tmp=D(a[i],s,x[i]);
            int fork=(tmp+D(a[i],s,t)-D(a[i],t,x[i]))/2;
            ans=F(a[i],s,t,d);
            if(fork>=d) ans+=(tmp-d+1)%mod*(siz[b[i]]%mod)%mod+F(b[i],y[i],y[i],0)%mod;
        }
    }
    return f[i][{s,t,d}]=ans%mod;
}
// F(i,s,t,d) -> sigma depth
map<tuple<int,int,int>,int>g[N];
inline int G(int i,int s,int t,int d){
    if(!d) return siz[i];
    if(g[i].count({s,t,d})) return g[i][{s,t,d}];
    int ans=0;
    if(s>siz[a[i]]&&t<=siz[a[i]]){
        int db=D(i,s,x[i]);
        if(d>=db) ans=G(a[i],x[i],t,d-db);
        else ans=G(b[i],s-siz[a[i]],y[i],d)+siz[a[i]];
    }
    else if(s<=siz[a[i]]&&t>siz[a[i]]){
        int da=D(i,s,y[i]+siz[a[i]]);
        if(d>=da) ans=G(b[i],y[i],t-siz[a[i]],d-da);
        else ans=G(a[i],s,x[i],d)+siz[b[i]];
    }
    else if(s>siz[a[i]]&&t>siz[a[i]]){
        s-=siz[a[i]]; t-=siz[a[i]];
        int tmp=D(b[i],s,y[i]);
        int fork=(tmp+D(b[i],s,t)-D(b[i],t,y[i]))/2;
        ans=G(b[i],s,t,d);
        if(fork>=d) ans+=siz[a[i]];
        s+=siz[a[i]]; t+=siz[a[i]];
    }
    else{
        int tmp=D(a[i],s,x[i]);
        int fork=(tmp+D(a[i],s,t)-D(a[i],t,x[i]))/2;
        ans=G(a[i],s,t,d);
        if(fork>=d) ans+=siz[b[i]];
    }
    return g[i][{s,t,d}]=ans%mod;
}
// G(i,s,t,d) -> sigma size
signed main(){
    freopen("link.in","r",stdin); freopen("link.out","w",stdout);
    m=read(); 
    dia[0]={1,1},siz[0]=1;
    rep(i,1,m){
        a[i]=read(),b[i]=read(),x[i]=read(),y[i]=read();
        siz[i]=siz[a[i]]+siz[b[i]];
        if(len[a[i]]>len[b[i]]) len[i]=len[a[i]],dia[i]=dia[a[i]];
        else len[i]=len[b[i]],dia[i]={dia[b[i]].fir+siz[a[i]],dia[b[i]].sec+siz[a[i]]};
        for(auto ver1:{dia[a[i]].fir,dia[a[i]].sec}){
            for(auto ver2:{dia[b[i]].fir,dia[b[i]].sec}){
                int nowlen=D(i,ver1,ver2+siz[a[i]]);
                if(nowlen>len[i]){
                    len[i]=nowlen;
                    dia[i]={ver1,siz[a[i]]+ver2};
                }
            }
        }
        int ans=0;
        if(len[i]&1){
            ans=add(F(i,dia[i].fir,dia[i].sec,(len[i]+1)/2),F(i,dia[i].sec,dia[i].fir,(len[i]+1)/2));
            ckadd(ans,(len[i]+1)/2%mod*(siz[i]%mod)%mod);
        }else{
            int cur=G(i,dia[i].fir,dia[i].sec,len[i]/2);
            ans=F(i,dia[i].fir,dia[i].sec,len[i]/2);
            ans+=F(i,dia[i].sec,dia[i].fir,len[i]/2+1)+(len[i]/2+1)%mod*(siz[i]%mod)-cur;
            ans=(ans%mod+mod)%mod;
        }
        print(ans);
    }
    return 0;
}

posted @   没学完四大礼包不改名  阅读(90)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示