省选模拟(20220320)

好几天之前的题了,但是当时只改了一道题,然后今天才改完哈

好像是p_b_p_b出的题,NOI第七,果然难,还很妙

然后我只能吐槽这个题有多么多么难,多么多么毒瘤......

考场上是咋想的忘了,反正看到第一题好像之前做过的一道,然而被加强了一万倍......

T1 签到

之前做的b非常的小,于是我祭了,梦中的题面

可以不向数位dp的方向想,我们直接按照数据范围不是高精的样子去想

这样就很容易想到容斥,枚举哪个盒子超过了限制,一般这样做的时候,我们是要恰好得到和是某一个数,然而这里是至少,没有关系,我们加入一个盒子,这个盒子就表示不选的球,并且容量没有限制

那么我们就有了一个\(\mathcal{O(2^n)}\)的做法,枚举哪几个盒子超出了限制,容斥一下就行了

然而难住我们的不是这里的推导,而是插板法的组合数...

我们都知道组合数\(n \choose m\)是一个关于n的m次多项式,都知道的

那么我们可以求每一项的系数,这个可以直接下降幂展开得到

然后我们发现n其实是一个定值A减去一个\(\sum b^i\)

这样我们的组合数就是关于后面求和的一个多项式了,我们可以用dp求后面的和的几次方

\(dp_{i,j,k}\)表示前i个数,选出来j个,和的k次方是多少,这个转移的时候可以二项式展开

我们现在要保证A>=后面的和,于是我们直接枚举A和后面求和的lcp

合并前面和后面,就是碰见一个大于1的直接break,碰见0就continue,碰见1就分类讨论

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=1005;
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 n,m,b,c,ans;
struct BIG{
    int x[105];
    BIG(){memset(x,0,sizeof(x));}
    BIG operator + (BIG a)const{
        BIG ret;ret.x[0]=max(x[0],a.x[0]);
        fo(i,1,ret.x[0]){
            ret.x[i]+=x[0]+a.x[0];
            ret.x[i+1]+=ret.x[i]/b;
            ret.x[i]%=b;
        }
        if(ret.x[ret.x[0]+1])ret.x[0]++;
        return ret;
    }
    BIG operator - (int a)const{
        BIG ret=*this;ret.x[1]-=a;
        fo(i,1,ret.x[0])if(ret.x[i]<0){
            ret.x[i]+=b,ret.x[i+1]--;
        }
        if(ret.x[ret.x[0]+1]<0)ret.x[0]=-1;
        return ret;
        if(!ret.x[ret.x[0]]&&ret.x[0]>1)ret.x[0]--;
        // if(ret.x[ret.x[0]]<0)ret.x[0]=-1;
        return ret;
    }
    BIG operator + (int a)const{
        BIG ret=*this;ret.x[1]+=a;
        fo(i,1,ret.x[0]){
            ret.x[i+1]+=(ret.x[i]/b);
            ret.x[i]%=b;
        }
        if(ret.x[ret.x[0]+1])ret.x[0]++;
        return ret;
    }
    BIG operator * (int a)const{
        BIG ret;ret.x[0]=x[0];
        fo(i,1,ret.x[0]){
            ret.x[i]+=x[i]*a;
            ret.x[i+1]+=ret.x[i]/b;
            ret.x[i]%=b;
        }
        if(ret.x[ret.x[0]+1])ret.x[0]++;
        return ret;
    }
    bool operator < (BIG a)const{
        if(x[0]!=a.x[0])return x[0]<a.x[0];
        fu(i,x[0],1){
            if(x[i]<a.x[i])return true;
            if(x[i]>a.x[i])return false;
        }
        return false;
    }
    void print(){
        fu(i,x[0],1)printf("%lld ",x[i]);
        printf("\n");
    }
}nn,cc;
char ch[N];int lim[N];
int dp[85][85][85],f[85][85];
int jc[N],inv[N],ji[N],xs[N],tmp[N];
int C(int x,int y){return jc[x]*inv[y]%mod*inv[x-y]%mod;}
void get_xs(int n){
    memset(xs,0,sizeof(xs));xs[0]=1;
    fo(i,0,m-1){
        int now=(n-i+mod)%mod;
        fo(j,0,m)tmp[j]=xs[j];
        fo(j,0,m)xs[j]=xs[j]*now%mod;
        fo(j,1,m)xs[j]=(xs[j]-tmp[j-1]+mod)%mod;
    }
    // int iv=ksm(m,mod-2);
    fo(i,0,m)xs[i]=xs[i]*inv[m]%mod;
}
signed main(){
    freopen("checkin.in","r",stdin);
    freopen("checkin.out","w",stdout);
    m=read();b=read();c=read();
    scanf("%s",ch+1);lim[0]=strlen(ch+1);
    fo(i,1,lim[0]){
        lim[i]=ch[lim[0]+1-i]-'0';
        n=(n+(ch[lim[0]+1-i]-'0')*ksm(10,i-1))%mod;
    }
    while(lim[0]){
        int ln=lim[0];lim[0]=0;
        fu(i,ln,1){
            lim[i-1]+=lim[i]%b*10;
            lim[i]/=b;
        }
        nn.x[++nn.x[0]]=lim[0]/10;lim[0]=ln;
        while(!lim[lim[0]]&&lim[0]>0)lim[0]--;
    }
    // nn.print();
    nn=nn+m;nn=nn-1;n=(n-1+m+mod)%mod;
    jc[0]=1;fo(i,1,m)jc[i]=jc[i-1]*i%mod;
    inv[0]=1;inv[m]=ksm(jc[m],mod-2);
    fu(i,m-1,1)inv[i]=inv[i+1]*(i+1)%mod;
    dp[0][0][0]=1;
    fo(i,0,m)fo(j,0,i){
        fo(k,0,m)dp[i+1][j][k]=(dp[i+1][j][k]+dp[i][j][k])%mod;
        fo(k,0,m){
            int v=ksm(b,i+1);
            ji[0]=1;fo(l,1,k)ji[l]=ji[l-1]*v%mod;
            fo(l,0,k){
                dp[i+1][j+1][k]=(dp[i+1][j+1][k]+C(k,l)*dp[i][j][l]%mod*ji[k-l])%mod;
            }
        }
    }
    // cerr<<dp[2][2][2]<<" "<<dp[2][1][2]<<endl;
    // cerr<<C(10,5)<<endl;
    int bas=1;
    fo(s,0,m){
        // cerr<<"n"<<" "<<n<<" "<<endl;
        get_xs(n);
        memset(f,0,sizeof(f));
        int sum=0;f[0][0]=1;
        fu(i,nn.x[0],2){
            if(sum==s)break;
            // cerr<<"f"<<" ";fo(k,0,m)cerr<<f[s][k]<<" ";cerr<<endl;
            if(nn.x[i]>1){
                fo(k,0,m)fo(l,0,k){
                    f[s][k]=(f[s][k]+C(k,l)*f[sum][l]%mod*dp[i-1][s-sum][k-l])%mod;
                }
                break;
            }
            if(nn.x[i]==0)continue;
            if(nn.x[i]==1){
                int v=ksm(b,i-1);
                ji[0]=1;fo(l,1,m)ji[l]=ji[l-1]*v%mod;
                fo(k,0,m)fo(l,0,k){
                    f[sum+1][k]=(f[sum+1][k]+C(k,l)*f[sum][l]%mod*ji[k-l])%mod;
                }
                fo(k,0,m)fo(l,0,k){
                    f[s][k]=(f[s][k]+C(k,l)*f[sum][l]%mod*dp[i-2][s-sum][k-l])%mod;
                }
                sum++;
            }
        }
        // fo(k,0,m)cerr<<xs[k]<<" ";cerr<<endl;
        // fo(k,0,m)cerr<<f[s][k]<<" ";cerr<<endl;
        fo(k,0,m)ans=(ans+bas*xs[k]*f[s][k]%mod+mod)%mod;
        // cerr<<ans<<endl;
        if(c>=1)nn=nn+(c-1);
        else nn=nn-(1-c);
        if(nn.x[0]==-1)break;
        bas=-bas;
        n=(n+(c-1)%mod+mod)%mod;
    }
    printf("%lld",ans);
}

T2 数据结构

发现最后求得的重心的子树和一定大于和的一半,于是将树拍扁中点一定在重心的子树里,于是倍增即可

AC_code
#include<bits/stdc++.h>
using namespace std;
#define ll 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=3e5+5;
int n,Q;ll al;
struct BIT{
    ll tr1[N],tr2[N];
    void insert(int x,ll v){
        for(int i=x;i<=n;i+=(i&-i)){
            tr1[i]+=v;tr2[i]+=1ll*v*(x-1);
        }
    }
    void ins(int l,int r,ll v){
        insert(l,v);insert(r+1,-v);
    }
    ll query(int x){
        ll ret=0;
        for(int i=x;i;i-=(i&-i)){
            ret+=tr1[i]*x-tr2[i];
        }return ret;
    }
    ll qry(int l,int r){
        return query(r)-query(l-1);
    }
}bit;
struct E{int to,nxt;}e[N*2];
int head[N],rp;
void add_edg(int x,int y){e[++rp].to=y;e[rp].nxt=head[x];head[x]=rp;}
int siz[N],son[N],top[N],dep[N],fa[N][20];
int dfn[N],dfm[N],idf[N],cnt;
void dfs_fi(int x,int f){
    dep[x]=dep[f]+1;
    siz[x]=1;son[x]=0;
    fa[x][0]=f;
    fo(i,1,19)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;
        if(y==f)continue;
        dfs_fi(y,x);siz[x]+=siz[y];
        if(!son[x]||siz[y]>siz[son[x]])son[x]=y;
    }
}
void dfs_se(int x,int f){
    top[x]=f;dfn[x]=++cnt;idf[cnt]=x;
    if(son[x])dfs_se(son[x],f);
    for(int i=head[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==fa[x][0]||y==son[x])continue;
        dfs_se(y,y);
    }dfm[x]=cnt;
}
void lian(int x,int y,ll v){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        bit.ins(dfn[top[x]],dfn[x],v);
        x=fa[top[x]][0];
    }
    if(dep[x]>dep[y])swap(x,y);
    bit.ins(dfn[x],dfn[y],v);
}
void subt(int x,ll v){
    bit.ins(dfn[x],dfm[x],v);
}
void work(){
    al=bit.qry(1,n);
    int l=1,r=n,mid;
    while(l<r){
        mid=l+r>>1;
        if(bit.qry(1,mid)<=(al>>1))l=mid+1;
        else r=mid;
    }
    int pos=idf[l];
    if(bit.qry(dfn[pos],dfm[pos])>al>>1){
        printf("%lld\n",pos);return ;
    }
    fu(i,19,0)if(fa[pos][i]&&bit.qry(dfn[fa[pos][i]],dfm[fa[pos][i]])<=al>>1)pos=fa[pos][i];
    printf("%lld\n",fa[pos][0]);
}
signed main(){
    freopen("yyl.in","r",stdin);
    freopen("yyl.out","w",stdout);
    n=read();
    fo(i,1,n-1){
        int x=read(),y=read();
        add_edg(x,y);add_edg(y,x);
    }
    dfs_fi(1,0);dfs_se(1,1);
    Q=read();
    while(Q--){
        int tp=read(),x=read(),y;ll v;
        if(tp==1){
            v=read();
            subt(x,v);
        }
        else {
            y=read();v=read();
            lian(x,y,v);
        }
        work();
    }
}

T3 爆搜

呵呵,状压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 mod=1e9+7;
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 n,nn,m,c[40],ans;
int e[40][40];
int lian[1<<18][40],huan[1<<18][40];
int l[1<<18],r[1<<18],f[1<<18],sum[1<<18];
signed main(){
    freopen("dfs.in","r",stdin);
    freopen("dfs.out","w",stdout);
    nn=n=read();m=read();c[1]=read();
    fo(i,2,n)c[i]=c[i-1]*c[1]%mod;
    fo(i,1,m){
        int x=read(),y=read();
        e[x][y]=e[y][x]=1;
    }
    n=(n+1)>>1<<1;
    int u=(1<<(n>>1))-1;
    fo(i,1,n>>1){
        lian[(1<<i-1)][i*2-1]=1;
        lian[(1<<i-1)][i*2]=1;
    }
    fo(s,0,u)fo(i,1,n){
        fo(j,1,n>>1)if(!(s>>j-1&1)){
            int x=j*2-1,y=j*2;
            if(e[x][i])lian[s|(1<<j-1)][y]=(lian[s|(1<<j-1)][y]+lian[s][i]*c[1])%mod;
            if(e[y][i])lian[s|(1<<j-1)][x]=(lian[s|(1<<j-1)][x]+lian[s][i]*c[1])%mod;
        }
    }
    fo(s,0,u){
        l[s]=0;
        fo(i,1,n)l[s]=(l[s]+lian[s][i])%mod;
        l[s]=l[s]*ksm(2,mod-2)%mod;
    }
    fo(h,1,n>>1){
        int v=(1<<h)-1;
        fo(s,0,v)fo(i,1,h<<1)huan[s][i]=0;
        huan[1<<h-1][h*2-1]=1;
        fo(s,0,v)fo(i,1,h<<1){
            fo(j,1,h)if(!(s>>j-1&1)){
                int x=j*2-1,y=j*2;
                if(e[x][i])huan[s|(1<<j-1)][y]=(huan[s|(1<<j-1)][y]+huan[s][i]*c[1])%mod;
                if(e[y][i])huan[s|(1<<j-1)][x]=(huan[s|(1<<j-1)][x]+huan[s][i]*c[1])%mod;
            }
        }
        fo(s,0,v){
            fo(i,1,h<<1)if(e[i][h<<1])r[s]=(r[s]+huan[s][i]*c[1])%mod;
        }
    }
    f[0]=1;
    fo(s,0,u){
        int v=u^s;
        for(int t=v;true;t=(t-1)&v){
            f[s|t]=(f[s|t]+f[t]*(l[s]+r[s]))%mod;
            if(t==0)break;
        }
    }
    printf("%lld",f[u]);
}
posted @ 2022-03-23 21:40  fengwu2005  阅读(37)  评论(0编辑  收藏  举报