1. 程式人生 > 其它 >【考試總結】2022-03-20

【考試總結】2022-03-20

簽到

列舉哪些位置超出了限制,注意這裡需要將 \(n\)\(1\),那麼可以直接列出答案的計算表示式

\[\sum_{S\subseteq U}(-1)^{|S|}\binom{n+m+(c-1)|S|-\sum\limits_{x\in S} b^{x}}{m} \]

如果我們可以保證 \(A=n+m+(c-1)|S|\ge \sum\limits_{x\in S} b^{x}\),那麼組合數是關於 \(\sum\limits_{x\in S} b^x\) 的多項式

列舉 \(|S|\) 以及 \(\sum\limits_{x\in S} b^x\)\(A\)\(\rm LCP\)

(十進位制)及後面一位的數字

這裡想要得到答案,還需要在低位求出關於 \(\sum b^i\) 的多項式在所有 \(S\) 中的每個次冪的和

\(\rm DP\) 來做:設 \(f_{k}[i][j]\) 表示考慮了 \(\{1,2\dots i\}\) 這些數字並選出了 \(j\)\(b^x\) 並對第 \(k\) 次冪的自變數求和,轉移使用二項式定理合並即可

注意需要寫高精度除法,但是進位制轉換隻用做一次,那麼使用模擬豎式的方法是可行的

Code Display
int Bas=10;
struct node{
    vector<int> a;
    inline int size(){return a.size();}
    inline void adjust(){
        int siz=a.size();
        for(int i=0;i<siz-1;++i) a[i+1]+=a[i]/Bas,a[i]%=Bas;
        while(a[siz-1]>=Bas){
            a.emplace_back(a[siz-1]/Bas);
            a[siz-1]%=Bas;
            ++siz;
        }
        while(siz>1&&!a[siz-1]) a.pop_back(),--siz;
        return ;
    }
    inline int toint(int Mod=mod){
        int siz=a.size(),res=0;
        for(int i=siz-1;i>=0;--i) res=(res*Bas+a[i])%Mod;
        return res;
    }
    inline void init(string x){
        vector<int> num[2]; num[0].resize(x.size());
        int cur=0;
        int len=x.size();
        for(int i=len-1;i>=0;--i) num[cur][i]=x[i]-'0';
        reverse(num[cur].begin(),num[cur].end());
        while(num[cur].size()>1||num[cur][0]>0){
            num[cur^1].clear();
            int tmp=0,siz=num[cur].size();
            bool flag=0;
            for(int i=siz-1;i>=0;--i){
                tmp=tmp*10+num[cur][i];
                if(tmp>=Bas) flag=1;
                if(flag) num[cur^1].emplace_back(tmp/Bas),tmp%=Bas;
            }
            a.emplace_back(tmp);
            cur^=1;
            reverse(num[cur].begin(),num[cur].end());
            siz=num[cur].size(); num[cur].emplace_back(0);
            for(int i=0;i<siz;++i){
                num[cur][i+1]+=num[cur][i]/10;
                num[cur][i]%=10;
            }
            while(num[cur][siz]) num[cur].emplace_back(0),++siz,num[cur][siz]+=num[cur][siz-1]/10,num[cur][siz-1]%=10;
            num[cur].pop_back();
        }
        return ;
    }
    inline void init(int x){
        a.clear();
        do{a.push_back(x%Bas),x/=Bas;}while(x);
        return ;
    }
    bool operator <(node b)const{
        if(a.size()!=b.size()) return a.size()<b.size();
        int len=a.size();
        for(int i=len-1;~i;--i) if(a[i]^b.a[i]) return a[i]<b.a[i];
        return 0;
    }
    bool operator ==(node b)const{
        if(b.size()!=a.size()) return 0;
        int len=a.size();
        for(int i=0;i<len;++i) if(a[i]!=b.a[i]) return 0;
        return 1;
    }
    bool operator <=(node &b)const{return *this<b||*this==b;}
    node operator +(node b)const{
        node res;
        int s1=a.size(),s2=b.a.size(); res.a.resize(max(s1,s2));
        for(int i=0;i<s1;++i) res.a[i]+=a[i];
        for(int i=0;i<s2;++i) res.a[i]+=b.a[i];
        res.adjust();
        return res;
    }
    node operator -(node b)const{
        node res=*this;
        int s1=a.size(),s2=b.size();
        for(int i=0;i<s2;++i){
            res.a[i]-=b.a[i];
            if(res.a[i]<0) res.a[i]+=Bas,res.a[i+1]--;
        }
        for(int i=s2;i<s1;++i) if(res.a[i]<0) res.a[i+1]--,res.a[i]+=Bas;
        res.adjust();
        return res;
    }
    node operator *(node b)const{
        node res; res.a.resize(a.size()+b.size()-1);
        for(int i=0;i<a.size();++i) for(int j=0;j<b.size();++j) res.a[i+j]+=a[i]*b.a[j];
        res.adjust();
        return res;
    }
    bool operator !=(node &b){return !(*this==b);}
    node operator *(const int &p)const{node t; t.init(p); return *this*t;}
    node operator +(const int &p)const{node t; t.init(p); return *this+t;}
    node operator -(const int &p)const{node t; t.init(p); return *this-t;}
    inline void output(){
        int siz=a.size(); printf("%lld ",a[siz-1]);
        putchar(' ');
        for(int i=siz-2;~i;--i) printf("%lld ",a[i]);
        return ;
    }

}n;
const int N=2010;
int poly[N],m,b,c,ans;
string str_n;
int pw[N][N],dp[100][100][100];
int ifac[N],C[N][N];
signed main(){
    // freopen("checkin.in","r",stdin); freopen("checkin.out","w",stdout);
    C[0][0]=ifac[0]=1;
    for(int i=1;i<=1000;++i){
        C[i][0]=1;
        ifac[i]=mul(ifac[i-1],ksm(i,mod-2));
        for(int j=1;j<=i;++j) C[i][j]=add(C[i-1][j],C[i-1][j-1]);
    }
    m=read(); b=read(); c=read();
    Bas=b;
    cin>>str_n;
    n.init(str_n); n=n-1;
    pw[0][0]=1; rep(i,1,2000) pw[0][i]=1;
    for(int i=1;i<=2000;++i){
        pw[i][0]=1;
        pw[i][1]=mul(pw[i-1][1],b);
        for(int j=2;j<=2000;++j) pw[i][j]=mul(pw[i][j-1],pw[i][1]);
    }
    dp[0][0][0]=1;
    for(int k=0;k<=m;++k){
        for(int i=1;i<=m;++i){
            for(int j=0;j<=i;++j){
                int sum=dp[i-1][j][k];
                if(j) for(int t=0;t<=k;++t) ckadd(sum,mul(C[k][t],mul(dp[i-1][j-1][t],pw[i][k-t])));
                dp[i][j][k]=sum;
            }
        }   
    }
    for(int S=0;S<=m;++S){
        node A,n_m,n_S,n_c; 
        n_m.init(m);
        n_S.init(S);
        n_c.init(abs(1-c));
        if(c<0&&n+n_m<n_c*n_S) continue;
        if(c<0) A=n+n_m-n_c*n_S;
        else A=n+n_m+n_c*n_S;
        int a=A.toint();
        int sum=0;
        vector<vector<int> >tmp(m);
        for(int i=0;i<m;++i) tmp[i].resize(i+2);
        tmp[0][0]=a; tmp[0][1]=mod-1;
        for(int i=1;i<m;++i){
            for(int j=0;j<=i+1;++j){
                if(j<=i) tmp[i][j]=mul(tmp[i-1][j],del(a,i));
                if(j) ckdel(tmp[i][j],tmp[i-1][j-1]);
            }
        }
        for(int i=0;i<=m;++i) poly[i]=mul(tmp[m-1][i],ifac[m]);
        auto evalue=[&](int x){
            int sum=0,pw=1;
            for(int i=0;i<=m;++i,ckmul(pw,x)) ckadd(sum,mul(poly[i],pw));
            return sum;
        };
        int hig=0,siz=A.size(),cnt=0;
        for(int lcp=siz-1;~lcp&&cnt<=S;--lcp){
            if(!lcp){
                if(cnt==S) ckadd(sum,evalue(hig));
                break;
            }
            int up=min(A.a[lcp],2ll);
            for(int cur=0;cur<up;++cur){
                if(cur==1) ++cnt,ckadd(hig,pw[lcp][1]);
                if(cnt>S) break;
                vector<int> pwh(1+m);
                pwh[0]=1;
                for(int i=1;i<=m;++i) pwh[i]=mul(pwh[i-1],hig);
                int tmp=0;
                for(int k=0;k<=m;++k){
                    int sumx=0;
                    for(int t=0;t<=k;++t) ckadd(sumx,mul(C[k][t],mul(pwh[k-t],dp[lcp-1][S-cnt][t])));
                    ckadd(tmp,mul(poly[k],sumx));   
                }
                ckadd(sum,tmp);
            }
            if(A.a[lcp]==1) ++cnt,ckadd(hig,pw[lcp][1]);
            if(A.a[lcp]>1) break;
        }
        if(S&1) ckdel(ans,sum); else ckadd(ans,sum); 
    }
    print(ans); putchar('\n');
    return 0;
}

資料結構

將樹拍到 \(\rm DFS\) 序上考慮,序列上的帶權中點一定在帶權重心的子樹內,寫倍增判定即可

卡常,所以用了 zkw線段樹

我還有一個非常麻煩的使用 新重心在兩個原來重心的路徑上 的結論的方法,比這個垃圾到不知道哪裡去了

Code Display
vector<int> G[N];
int n;
int fa[N],dep[N],siz[N],top[N],son[N],dfn[N],ord[N],tim;
int bz[N][20];
inline void dfs2(int x,int topf){
    top[x]=topf; ord[dfn[x]=++tim]=x; if(son[x]) dfs2(son[x],topf);
    for(auto t:G[x]) if(!dfn[t]) dfs2(t,t);
}
inline void dfs1(int x,int fat){
    dep[x]=dep[fa[x]=fat]+(siz[x]=1);
    bz[x][0]=fat;
    for(int i=1;bz[x][i-1];++i) bz[x][i]=bz[bz[x][i-1]][i-1];
    for(auto t:G[x]) if(t!=fat){
        dfs1(t,x); siz[x]+=siz[t];
        if(siz[t]>siz[son[x]]) son[x]=t;
    } return ;
}
ll nec;
struct Seg{
    #define ls p<<1
    #define rs p<<1|1
    int lim;
    ll sum[N<<2],tag[N<<2];
    inline void build(int n){
        lim=1;
        while(lim<=(n+1)) lim<<=1;
        return ;
    }
    inline void upd(int l,int r,ll v){
        l+=lim-1; r+=lim+1;
        int lc=0,rc=0;
        for(int len=1;l!=r-1;l>>=1,r>>=1,len<<=1){
            sum[l]+=lc*v; sum[r]+=rc*v;
            if(!(l&1)) sum[l^1]+=len*v,tag[l^1]+=v,lc+=len;
            if(r&1) sum[r^1]+=len*v,tag[r^1]+=v,rc+=len;
        }
        while(l){
            sum[l]+=lc*v; sum[r]+=rc*v;
            l>>=1; r>>=1;
        }
        return ;
    }
    inline ll Query(int l,int r){
        ll res=0,rc=0,lc=0;
        l+=lim-1; r+=lim+1;
        for(int len=1;l!=r-1;r>>=1,l>>=1,len<<=1){
            res+=tag[l]*lc+tag[r]*rc;
            if(r&1) res+=sum[r^1],rc+=len;
            if(!(l&1)) res+=sum[l^1],lc+=len;
        }
        while(l){
            res+=tag[l]*lc+tag[r]*rc;
            l>>=1; r>>=1;
        }
        return res;
    }
    #undef ls
    #undef rs
    #undef lson
    #undef rson
}T;
signed main(){
    freopen("yyl.in","r",stdin); freopen("yyl.out","w",stdout);
    n=read(); 
    rep(i,1,n-1){
        int u=read(),v=read();
        G[u].push_back(v); G[v].emplace_back(u);
    }
    int Q=read();
    dfs1(1,0); dfs2(1,1); T.build(n);
    while(Q--){
        if(read()-1){
            int u=read(),v=read(),w=read();
            while(top[u]!=top[v]){
                if(dep[top[u]]<dep[top[v]]) swap(u,v); 
                T.upd(dfn[top[u]],dfn[u],w); 
                u=fa[top[u]];
            }
            if(dep[u]>dep[v]) swap(u,v);
            T.upd(dfn[u],dfn[v],w);
        }else{
            int x=read(),w=read();
            T.upd(dfn[x],dfn[x]+siz[x]-1,w);
        }
        nec=T.sum[1]/2+1; //找到第一個大於等於 nec 的點即可 
        int l=1,r=n-1,pos=n;
        while(l<=r){
            int mid=(l+r)>>1;
            if(T.Query(1,mid)>=nec) pos=mid,r=mid-1;
            else l=mid+1;
        }
        int ans=ord[pos];
        for(int i=19;~i;--i) if(bz[ans][i]){
            int tar=bz[ans][i];
            if(T.Query(dfn[tar],dfn[tar]+siz[tar]-1)<=T.sum[1]/2) ans=bz[ans][i];
        }
        if(T.Query(dfn[ans],dfn[ans]+siz[ans]-1)<=T.sum[1]/2) ans=fa[ans];
        print(ans);
    }
    return 0;
}

爆搜

觀察到 \(n\) 個點的無向圖最多有 \(\frac n2\) 組匹配,將 \((2i,2i+1)\) 連一條虛邊,那麼所有的合法匹配一定將點集劃分成了若干個環和鏈

使用虛邊將 \((2i,2i+1)\) 繫結成一個虛點,使用 \(\Theta(2^{\frac n2}n^2)\) 的狀壓 \(\rm DP\) 來求每個集合表示成鏈/環的權值之和,剩下的工作是一個集合冪級數 \(\exp\)

\(\rm DP\) 鏈新增一維記錄鏈尾,轉移時因為 \((2i,2i+1)\) 被繫結,新增一組虛點的時候可以據此得到新的鏈尾,注意每條鏈會被計算正反兩次

計算環的方案數時從環上最大標號的點開始 \(\rm DP\),加入的一對虛點標號不超過當前集合最大值即可,也要記錄環尾,最後通過環尾和集合內最大值是否有邊判定是否合法即可

Code Display
const int N=40;
int n,m,C,G[N][N],inv[N],ifac[N],fac[N];
int chain[1<<18],cyc[1<<18],dp[1<<18][40];

inline int in(int S,int id){
    return S>>(id>>1)&1;
}
struct FPS{
    int p[19]; FPS(){memset(p,0,sizeof(p));}
    FPS operator +(const FPS &a)const{
        FPS res; 
        rep(i,0,m) res.p[i]=add(p[i],a.p[i]);
        return res;
    }
    FPS operator -(const FPS &a)const{
        FPS res; 
        rep(i,0,m) res.p[i]=del(p[i],a.p[i]);
        return res;
    }
}f[1<<18];
inline void Exp(int *f){
    vector<int> g(m+1);
    g[0]=1;
    for(int i=1;i<=m;++i){
        for(int j=0;j<i;++j) ckadd(g[i],mul(f[j+1],mul(g[i-j-1],j+1)));
        ckmul(g[i],inv[i]);
    }
    for(int i=0;i<=m;++i) f[i]=g[i];
    return ;
}
signed main(){
    freopen("dfs.in","r",stdin); freopen("dfs.out","w",stdout);
    n=39; inv[0]=fac[0]=1;
    for(int i=1;i<=n;++i) fac[i]=mul(fac[i-1],i);
    ifac[n]=ksm(fac[n],mod-2);
    Down(i,n,1) ifac[i-1]=mul(ifac[i],i),inv[i]=mul(ifac[i],fac[i-1]);
    n=read(); m=read(); C=read(); if(n&1) ++n;
    while(m--){
        int u=read()-1,v=read()-1;
        G[u][v]++; G[v][u]++;
    }
    m=(n+1)/2;
    int S=1<<m;
    for(int i=0;i<m;++i){
        dp[1<<i][i<<1]=1;
        if(2*i+1<=n) dp[1<<i][i<<1|1]=1;
    }
    for(int i=0;i<S;++i){
        for(int j=0;j<n;++j) if(dp[i][j]){
            for(int k=0;k<n;++k) if(!in(i,k)&&G[j][k]){
                ckadd(dp[i^(1<<(k>>1))][k^1],mul(C,dp[i][j]));
            }
        }
    }
    for(int i=1;i<S;++i){
        for(int j=0;j<n;++j) ckadd(chain[i],dp[i][j]);
        ckmul(chain[i],inv[2]);
    } 
    memset(dp,0,sizeof(dp));
    for(int i=0;i<m;++i) dp[1<<i][i<<1]=1;
    for(int i=1;i<S;++i){
        int hig=63-__builtin_clzll(i);
        for(int j=0;j<=hig*2+1;++j) if(dp[i][j]){
            for(int k=0;k<=hig*2+1;++k) if(!in(i,k)&&G[j][k]){
                ckadd(dp[i^(1<<(k>>1))][k^1],mul(C,dp[i][j]));
            }
        }
    }
    for(int i=1;i<S;++i){
        int hig=63-__builtin_clzll(i);
        for(int j=0;j<=2*hig+1;++j) ckadd(cyc[i],mul(G[j][hig<<1|1],dp[i][j]));
        ckmul(cyc[i],C);
    } 
    for(int i=0;i<S;++i) f[i].p[__builtin_popcount(i)]=add(cyc[i],chain[i]);
    for(int p=2;p<=S;p<<=1){
        int len=p>>1;
        for(int k=0;k<S;k+=p) for(int l=k;l<k+len;++l) f[l+len]=f[l+len]+f[l];
    }
    for(int i=0;i<S;++i) Exp(f[i].p);
    for(int p=2;p<=S;p<<=1){
        int len=p>>1;
        for(int k=0;k<S;k+=p) for(int l=k;l<k+len;++l) f[l+len]=f[l+len]-f[l];
    }
    print(f[S-1].p[m]);
    return 0;
}