2021牛客暑期多校訓練營8 部分題題解


D.OR

題目鏈接

OR

簡要題解

仔細觀察可以發現

\[c_i-b_i=(a_{i-1}+a_i)-(a_{i-1}|a_i)=a_{i-1} \& a_i \]

那么我們令\(d_i=c_i-b_i=a_{i-1}\&a_i\),再令\(e_i=d_i\bigoplus b_i=a_{i-1}\bigoplus a_i\)
這時我們對限制進行了轉換,所有限制都成了位運算的形式,就可以按位考慮合法性了。

我們枚舉\(a_i\)的每一位上的值,再利用\(e_i\)確定整個\(a_i\)在該位上的值,然后檢驗是否合法即可。
最終的答案就是每一位上方案的乘積。
代碼如下:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+10;
int n,Ans=1,A[MAXN],B[MAXN],C[MAXN],D[MAXN],E[MAXN];
int Read()
{   int a=0,c=1;   char b=getchar();
    while(b!='-'&&(b<'0'||b>'9')) b=getchar();
    if(b=='-') c=-1,b=getchar();
    while(b>='0'&&b<='9') a=a*10+b-48,b=getchar();
    return a*c;
}
int Calc(int Bit,int St)
{   A[1]=St*Bit;
    for(int i=2;i<=n;i++)
    {   A[i]=(E[i]^A[1])&Bit;
        if((A[i]|A[i-1])!=(B[i]&Bit)) return 0;
    }
    return 1;
}
int main()
{   n=Read();
    for(int i=2;i<=n;i++) B[i]=Read();
    for(int i=2;i<=n;i++) C[i]=Read();
    for(int i=2;i<=n;i++) D[i]=C[i]-B[i],E[i]=B[i]^D[i]^E[i-1];
    for(int i=0;i<=30;i++) Ans*=Calc(1<<i,0)+Calc(1<<i,1);
    printf("%d\n",Ans);
}

F.Robots

題目鏈接

Robots

簡要題解

對於前兩種機器人,我們可以預處理出某個點最下和最右能走到哪兒,然后直接回答,因此下文只考慮第三種機器人。
這道題的暴力做法是\(O(q*n^2)\)的,即對每一個詢問的矩形,直接遍歷一次。
實際上我們發現,詢問的矩形有很多重疊的地方,這些信息是可以重復利用的。
重復利用信息,那么就需要把多個詢問一起處理,自然會想到分治,我們按行來分治。

假設我們當前需要處理第\(L\)行到第\(R\)行內的詢問矩形,那么我們令\(Mid=(L+R)/2\)
當前我們只處理起點在\([L,Mid]\)行,終點在\([Mid,R]\)行的詢問,其余的詢問可以繼續遞歸分治處理。
我們對於第\([L,Mid]\)行中的每個點\((i,j)\),求出它能走到第\(Mid\)行中的哪些點,設這個點集為\(F[i][j]\)
對於\([Mid,Ri]\)行中的每個點\((i,j)\),求出第\(Mid\)行有哪些點可以走到它,設這個點集為\(G[i][j]\)
那么對於一個詢問來說,我們只要判斷\(F[X1][Y1]\)\(G[X2][Y2]\)是否有交即可。
\(F[i][j]\)\(G[i][j]\)可以直接轉移,單次求\(F\)\(G\)的時間復雜度是\(O(n^3)\)的,由於分治每一層都得求,總復雜度為\(O(n^3logn)\)
處理一個詢問的復雜度是\(O(n)\)的,處理所有詢問的復雜度是\(O(q*n)\)的。
遞歸過程中需要分治詢問,這部分的復雜度為\(O(q*logn)\)

實際上,我們對\(F\)\(G\)的操作都可以用\(bitset\)實現,因此直接用\(bitset\)優化,時間復雜度可以除上一個\(\omega\)
總復雜度為\(O(q*logn+\frac{q*n+n^3logn}{\omega})\)
代碼如下:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=510;
const int MAXQ=5e5+10;
struct ELE{   int X1,Y1,X2,Y2;   }Q[MAXQ];
char Mat[510][510];
int n,m,Ls,Qs,Ans[MAXQ],Seq[MAXQ],Td[MAXQ];
int Right[MAXN][MAXN],Down[MAXN][MAXN];
bitset<MAXN>F[MAXN][MAXN],G[MAXN][MAXN],Zero;
int Read()
{   int a=0,c=1;   char b=getchar();
    while(b!='-'&&(b<'0'||b>'9')) b=getchar();
    if(b=='-') c=-1,b=getchar();
    while(b>='0'&&b<='9') a=a*10+b-48,b=getchar();
    return a*c;
}
void Divide(int Le,int Ri,int Sl,int Sr)
{   if(Sl>Sr||Le>Ri) return ;
    int Mid=(Le+Ri)/2,Nl=Sl,Nr=Sr;
    for(int i=m,j=1;i>=1;i--,j++)
    {   F[Mid][i].reset(),G[Mid][j].reset();
        if(Mat[Mid][i]=='0') F[Mid][i].set(i),F[Mid][i]|=F[Mid][i+1];
        if(Mat[Mid][j]=='0') G[Mid][j].set(j),G[Mid][j]|=G[Mid][j-1];
    }
    for(int i=Mid-1;i>=Le;i--)
        for(int j=m;j>=1;j--)
            F[i][j]=(Mat[i][j]=='0'?F[i][j+1]|F[i+1][j]:Zero);
    for(int i=Mid+1;i<=Ri;i++)
        for(int j=1;j<=m;j++)
            G[i][j]=(Mat[i][j]=='0'?G[i][j-1]|G[i-1][j]:Zero);
    for(int i=Sl;i<=Sr;i++)
        if(Q[Seq[i]].X2<Mid) Td[Nl++]=Seq[i];
        else if(Q[Seq[i]].X1>Mid) Td[Nr--]=Seq[i];
        else Ans[Seq[i]]=(F[Q[Seq[i]].X1][Q[Seq[i]].Y1]&G[Q[Seq[i]].X2][Q[Seq[i]].Y2]).any();
    for(int i=Sl;i<=Sr;i++) Seq[i]=Td[i];
    Divide(Le,Mid-1,Sl,Nl-1),Divide(Mid+1,Ri,Nr+1,Sr);
}
int main()
{   n=Read(),m=Read();
    for(int i=1;i<=n;i++) scanf("%s",Mat[i]+1);
    for(int i=n;i>=1;i--)
        for(int j=m;j>=1;j--)
        {   Right[i][j]=(Mat[i][j+1]=='0'?Right[i][j+1]:j);
            Down[i][j]=(Mat[i+1][j]=='0'?Down[i+1][j]:i);
        }
    Qs=Read();
    for(int i=1;i<=Qs;i++)
    {   int T=Read(),X1=Read(),Y1=Read(),X2=Read(),Y2=Read();
        if(X1>X2||Y1>Y2||Mat[X1][Y1]!='0'||Mat[X2][Y2]!='0') continue ;
        if(T!=3) Ans[i]=(T==1?Down[X1][Y1]>=X2&&Y1==Y2:Right[X1][Y1]>=Y2&&X1==X2);
        else Q[i]=(ELE){X1,Y1,X2,Y2},Seq[++Ls]=i;
    }
    Divide(1,n,1,Ls);
    for(int i=1;i<=Qs;i++) puts(Ans[i]?"yes":"no");
}

H.Scholomance Academy

題目鏈接

Scholomance Academy

簡要題解

我們知道

\[G(N)=\sum_{\sum_{i=1}^tk_i=N}F(\prod_{i=1}^tp_i^{k_i}) \]

\[F(n)=\sum_{\prod_{i=1}^ma_i=n}\prod_{i=1}^m\varphi(a_i) \]

現在給定\(N,t,m\)和質數序列\(\{p_i\}\),求\(G(N)\)

\(G(N)\)表達式中關於\(F(n)\)的一塊很是復雜,我們考慮簡化。
通過觀察發現,\(F(x)\)是由\(m\)\(\varphi(x)\)卷積得到的,那么\(F(x)\)就是積性函數,我們可以單獨考慮每種質因子的值。
於是我們可以改寫\(G(N)\)的表達式:

\[G(N)=\sum_{\sum_{i=1}^tk_i=N}\prod_{i=1}^tF(p_i^{k_i}) \]

又因為\(\sum_{i=1}^tk_i=N\),我們可以考慮構造生成函數\(g(x)=\sum_{i=0}^{\infty}G(i)*x^i\),我們要求的就是\(g(x)\)\(x^N\)項對應系數。
那么\(g(x)\)就應該是由\(t\)個函數卷積得到的,我們對每一種質因子\(p_i\)構造一個生成函數\(q_i(x)=\sum_{j=0}^{\infty}F(p_i^j)*x^j\)
之前提到,\(F(x)\)\(m\)\(\varphi(x)\)的卷積,那么\(q_i(x)=(\sum_{j=0}^{\infty}\varphi(p_i^j)*x^j)^m\)

考慮化簡\(q_i(x)\)

\[q_i(x)=(1+\sum_{j=1}^{\infty}\varphi(p_i^j)*x^j)^m=(1+\sum_{j=1}^{\infty}p_i^{j-1}*(p_i-1)*x^j)^m=(1+\frac{p_i-1}{p_i}\sum_{j=1}^{\infty}p_i^j*x^j)^m \]

等比數列求和得到

\[q_i(x)=(1+\frac{p_i-1}{p_i}*\frac{p_i*x}{1-p_i*x})^m=(\frac{1-x}{1-p_i*x})^m \]

那么

\[g(x)=\prod_{i=1}^{t}q_i(x)=\prod_{i=1}^t(\frac{1-x}{1-p_i*x})^m \]

於是\(g(x)\)可以寫成線性遞推的形式,套上常系數齊次線性遞推的模板就可以求出\(G(N)\)了。

關於線性遞推,具體地說,我們令\(b(x)=\prod_{i=1}^{t}(1-x)^m=\sum_{i=0}^{\infty}B_i*x^i\)\(a(x)=\prod_{i=1}^{t}(1-p_i*x)^m=\sum_{i=0}^{\infty}A_i*x^i\)
那么\(g(x)=\frac{b(x)}{a(x)}\),有\(b(x)=g(x)*a(x)\)\(B_n=\sum_{i=0}^{n}A_i*G_{n-i}\)
由於\(a(x)\)\(b(x)\)最高次冪都是\(m*t\),那么當\(n>m*t\)\(B_n=\sum_{i=0}^{m*t}A_i*G_{n-i}=0\)
得到\(A_0*G_n+\sum_{i=1}^{m*t}A_i*G_{n-i}=0\),改寫一下就變成了

\[G_n=\sum_{i=1}^{m*t}\frac{-A_i}{A_0}G_{n-i} \]

這就是線性遞推的經典形式了,\(a(x)\)\(b(x)\)可以用分治\(NTT\)解決,\(g(x)\)的前\(m*t\)項可以直接多項式求逆計算\(\frac{b(x)}{a(x)}\)
雖說數據范圍寫着\(t\leq 10^5\),但是\(10^5\)以內的質數只有\(9592\)個,因此\(t\)不可能超過\(9592\),不加優化的線性遞推能夠跑過去。
時間復雜度為\(O(m*t*logn*log(m*t))\)
代碼如下:

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
const int MAXN=1<<18;
const int Mod=998244353;
int N,t,m,Ans,P[MAXN],A[MAXN],B[MAXN],G[MAXN];
int Pow(int Down,int Up)
{   int Ret=1,Now=Down;
    while(Up) Up&1?Ret=1ll*Ret*Now%Mod:0,Now=1ll*Now*Now%Mod,Up>>=1;
    return Ret;
}
int Add(int A,int B){   return A+=B,A>=Mod?A-Mod:A;   }
namespace Poly
{   int A[MAXN],B[MAXN],Rader[MAXN],Len,Ms,Inv,Il;
    int R[MAXN],Q[MAXN],Xr[MAXN],Yr[MAXN],Now[MAXN];
    int F[MAXN],G[MAXN],C[MAXN],K;
    void NTT(int *P,int K)
    {   for(int i=0;i<Len;i++)
            if(i<Rader[i]) swap(P[i],P[Rader[i]]);
        for(int i=1;i<Len;i<<=1)
        {   int Euler=Pow(3,(Mod-1)/(i<<1));
            if(K<0) Euler=Pow(Euler,Mod-2);
            for(int Pos=i<<1,j=0;j<Len;j+=Pos)
            {   int Wi=1;
                for(int k=0;k<i;k++,Wi=1ll*Wi*Euler%Mod)
                {   int X=P[j+k],Y=1ll*Wi*P[i+j+k]%Mod;
                    P[j+k]=Add(X,Y),P[i+j+k]=Add(X,Mod-Y);
                }
            }
        }
        if(K>0) return ;
        Inv=Pow(Len,Mod-2);
        for(int i=0;i<Len;i++) P[i]=1ll*P[i]*Inv%Mod;
    }
    void Prepare(int Lx)
    {   Ms=-1;
        for(Len=1;Len<=Lx;Len<<=1) Ms++;
        for(int i=0;i<Len;i++) Rader[i]=(Rader[i>>1]>>1)|((i&1)<<Ms);
    }
    void Get_Mul(int *X,int *Y,int Lx,int Ly)
    {   Prepare(Lx+Ly);
        for(int i=0;i<Len;i++) A[i]=B[i]=0;
        for(int i=0;i<=Lx;i++) A[i]=X[i];
        for(int i=0;i<=Ly;i++) B[i]=Y[i];
        NTT(A,1),NTT(B,1);
        for(int i=0;i<Len;i++) A[i]=1ll*A[i]*B[i]%Mod;
        NTT(A,-1);
    }
    void Get_Inv(int *X,int *Y,int Lx)
    {   if(Lx==0) return Y[0]=Pow(X[0],Mod-2),(void)0;
        Get_Inv(X,Y,Lx>>1),Prepare(Lx<<1);
        for(int i=0;i<=Lx;i++) A[i]=X[i],B[i]=Y[i];
        for(int i=Lx+1;i<Len;i++) A[i]=B[i]=0;
        NTT(A,1),NTT(B,1);
        for(int i=0;i<Len;i++) A[i]=1ll*A[i]*B[i]%Mod*B[i]%Mod;
        NTT(A,-1);
        for(int i=0;i<=Lx;i++) Y[i]=Add(Y[i],Add(Y[i],Mod-A[i]));
    }
    void Get_Remain(int *X,int *Y,int Lx,int Ly)
    {   if(Lx<Ly) return ;
        for(Il=1;Il<=Lx-Ly+1;Il<<=1) ;
        for(int i=0;i<=Il;i++) Xr[i]=Yr[i]=Q[i]=0;
        for(int i=0;i<=Lx;i++) Xr[i]=X[Lx-i];
        for(int i=0;i<=Ly;i++) Yr[i]=Y[Ly-i];
        Get_Inv(Yr,Q,Il-1);
        for(int i=Lx-Ly+1;i<Il;i++) Q[i]=0;
        Get_Mul(Xr,Q,Il-1,Il-1);
        for(int i=0;i<=Lx-Ly;i++) Q[i]=A[Lx-Ly-i];
        for(int i=Lx-Ly+1;i<Il;i++) Q[i]=0;
        for(Il=1;Il<=Lx+1;Il<<=1) ;
        Get_Mul(Q,Y,Il-1,Il-1);
        for(int i=0;i<Il;i++) X[i]=Add(X[i],Mod-A[i]);
    }
    void Poly_Pow(int *Ret,int Up)
    {   Ret[0]=1,Now[1]=1;
        for(;Up;Up>>=1)
        {   if(Up&1)
            {   Get_Mul(Ret,Now,K-1,K-1);
                for(int i=0;i<=2*K-2;i++) Ret[i]=A[i];
                Get_Remain(Ret,G,2*K-2,K);
            }
            Get_Mul(Now,Now,K-1,K-1);
            for(int i=0;i<=2*K-2;i++) Now[i]=A[i];
            Get_Remain(Now,G,2*K-2,K);
        }
    }
    void Recursive(int *F,int *A)
    {   K=m*t+1;
        for(int i=0;i<K;i++) G[i]=Add(0,Mod-A[K-i]);
        G[K]=1,Poly_Pow(C,N);
        for(int i=0;i<K;i++) Ans=Add(Ans,1ll*C[i]*F[i]%Mod);
    }
}using Poly::Poly_Pow;using Poly::Get_Mul;using Poly::Recursive;using Poly::Get_Inv;
namespace PRE
{   struct ELE{   int Np,Len;   }Na,Nb;
    bool operator < (ELE A,ELE B){   return A.Len>B.Len;   }
    priority_queue<ELE>Heap;
    vector<int>S[MAXN/10];
    int Ha[MAXN],Hb[MAXN];
    void Divide(int *W)
    {   while(Heap.size()>=2)
        {   Na=Heap.top(),Heap.pop(),Nb=Heap.top(),Heap.pop();
            for(int i=0;i<Na.Len;i++) Ha[i]=S[Na.Np][i];
            for(int i=0;i<Nb.Len;i++) Hb[i]=S[Nb.Np][i];
            Get_Mul(Ha,Hb,Na.Len-1,Nb.Len-1),S[Na.Np].clear(),S[Nb.Np].clear();
            for(int i=0;i<=Na.Len+Nb.Len-2;i++) S[Na.Np].pb(Poly::A[i]);
            Heap.push((ELE){Na.Np,Na.Len+Nb.Len-1});
        }
        Na=Heap.top(),Heap.pop();
        for(int i=0;i<=t;i++) W[i]=Hb[i]=S[Na.Np][i];
        for(int i=2;i<=m;i++)
        {   for(int j=0;j<=(i-1)*t;j++) Ha[j]=W[j];
            Get_Mul(Ha,Hb,(i-1)*t,t);
            for(int j=0;j<=i*t;j++) W[j]=(Poly::A[j]);
        }
    }
    void Prepare()
    {   for(int i=1;i<=t;i++) S[i].pb(1),S[i].pb(Mod-P[i]),Heap.push((ELE){i,2});
        Divide(A);
        for(int i=1;i<=t;i++) S[i].clear(),S[i].pb(1),S[i].pb(Mod-1),Heap.push((ELE){i,2});
        Divide(B);
        for(int i=0;i<=m*t;i++) Ha[i]=0;
        Get_Inv(A,Ha,m*t),Get_Mul(Ha,B,m*t,m*t);
        for(int i=0;i<=m*t;i++) G[i]=(Poly::A[i]);
        if(N<=m*t) printf("%d\n",G[N]),exit(0);
        for(int i=1,Ni=Pow(A[0],Mod-2);i<=m*t;i++) A[i]=Add(0,Mod-1ll*A[i]*Ni%Mod);
    }
}using PRE::Prepare;
int main()
{   scanf("%d%d%d",&N,&t,&m);
    for(int i=1;i<=t;i++) scanf("%d",&P[i]);
    Prepare(),Recursive(G,A),printf("%d\n",Ans);
}

J.Tree

題目鏈接

Tree

簡要題解

我們把博弈雙方設為\(S\)\(T\)\(S\)先手,設游戲結束時,\(S\)\(T\)的移動步數為\(F(S)\)\(F(T)\)
那么\(S\)希望\(F(S)-F(T)\)盡量大,\(T\)希望這個值盡量小。
也就是說,決策時需要考慮兩個因素,自己走的步數盡量多,別人走的步數盡量少。

假設當前決策為\(S\),那么\(S\)有兩種決策,要么\(S\)朝着\(T\)移動,要么\(S\)朝着其他方向的一條最長鏈移動。
如果\(S\)沒有朝着\(T\)移動,那么\(S\)\(T\)就再也不會相互影響決策,只能各自按着最長鏈移動。
於是我們可以在樹上把\(S\)\(T\)的這條鏈單獨拉出來,再處理出鏈上每個點不經過鏈,所能走到的最長鏈的長度。
我們用堆來維護\(S\)\(T\)當前所能走到的最長鏈長度,然后模擬決策過程。
如果\(S\)沒有朝着\(T\)移動,那么我們可以立馬計算貢獻,否則\(S\)朝着\(T\)走一步,輪到\(T\)決策,這個貢獻遞歸處理即可。
\(S\)決策時,會取兩種情況中貢獻較大的那一個,\(T\)決策時會取較小的那一個。
時間復雜度\(O(nlogn)\)
代碼如下:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=5e5+10;
struct EDGE{   int u,v,Next;   }Edge[MAXN*2];
bool Vis[MAXN];
int n,S,T,Es,Ps,First[MAXN];
int Fa[MAXN],Deep[MAXN];
int Path[MAXN],Long[MAXN];
priority_queue<int>Hs,Ds,Ht,Dt;
int Read()
{   int a=0,c=1;   char b=getchar();
    while(b!='-'&&(b<'0'||b>'9')) b=getchar();
    if(b=='-') c=-1,b=getchar();
    while(b>='0'&&b<='9') a=a*10+b-48,b=getchar();
    return a*c;
}
int Min(int A,int B){   return A<B?A:B;   }
int Max(int A,int B){   return A>B?A:B;   }
void Link(int u,int v){   Edge[++Es]=(EDGE){u,v,First[u]},First[u]=Es;   }
void Dfs(int Now,int Ba)
{   Fa[Now]=Ba;
    for(int i=First[Now],v;i!=-1;i=Edge[i].Next)
        if((v=Edge[i].v)!=Ba) Dfs(v,Now);
}
int Get_Path(int Np){   return Path[++Ps]=Np,Vis[Np]=1,Np==T?0:Get_Path(Fa[Np]);   }
int Dfs2(int Now,int Nd)
{   int Ret=Nd;
    for(int i=First[Now],v;i!=-1;i=Edge[i].Next)
        if(!Vis[v=Edge[i].v]) Vis[v]=1,Ret=Max(Ret,Dfs2(v,Nd+1));
    return Ret;
}
int Dpt(int Ns,int Nt);
int Dps(int Ns,int Nt)
{   if(Ns+1==Nt) return Long[Ns]-Long[Nt];
    Ds.push((Ns-1+Long[Ns])),Dt.push(Long[Ns]+Ps-Ns);
    while(!Ht.empty()&&!Dt.empty()&&Ht.top()==Dt.top()) Ht.pop(),Dt.pop();
    while(!Hs.empty()&&!Ds.empty()&&Hs.top()==Ds.top()) Hs.pop(),Ds.pop();
    return Max(Long[Ns]-Ht.top()+Ps-Nt,Dpt(Ns+1,Nt)+1);
}
int Dpt(int Ns,int Nt)
{   if(Ns+1==Nt) return Long[Ns]-Long[Nt];
    Ds.push((Nt-1+Long[Nt])),Dt.push(Long[Nt]+Ps-Nt);
    while(!Hs.empty()&&!Ds.empty()&&Hs.top()==Ds.top()) Hs.pop(),Ds.pop();
    while(!Ht.empty()&&!Dt.empty()&&Ht.top()==Dt.top()) Ht.pop(),Dt.pop();
    return Min(Hs.top()-Ns+1-Long[Nt],Dps(Ns,Nt-1)-1);
}
int main()
{   n=Read(),S=Read(),T=Read();
    memset(First,-1,sizeof(First));
    for(int i=1,u,v;i<n;i++) u=Read(),v=Read(),Link(u,v),Link(v,u);
    Dfs(T,T),Get_Path(S);
    for(int i=1;i<=Ps;i++) Long[i]=Dfs2(Path[i],0);
    for(int i=1;i<=Ps;i++) Ht.push(Long[i]+Ps-i);
    for(int i=1;i<=Ps;i++) Hs.push(i-1+Long[i]);
    printf("%d\n",Dps(1,Ps));
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM