2019年7月26日(数学DP)

Posted alanallen21love28

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2019年7月26日(数学DP)相关的知识,希望对你有一定的参考价值。

难受,爆零!!

哎……讲题吧

prob1:A

题目大意:两种操作:把某数二进制上某一位翻转或异或一个集合中的一个数,求从\(s\)变到\(t\)的最少步数

\(sb\)题,完完全全的水题,结果\(bfs\)的队列写萎了

第一种操作可以转成第二种,而每个数最多只被异或一次,一通\(bfs\)瞎搞就可以了。但最小步数更新需在入队时,并将是否更新作为入队条件,否则会\(T,RE\)炸掉(别问我怎么知道的)

贴代码:

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define in read()
#define fur(i,a,b) for(int i=a;i<=b;++i)
#define int long long
#define xx 270000
const int mod=998244353;
inline int read()

    int x=0,f=1;char ch=getchar();
    for(;!isalnum(ch);ch=getchar()) if(ch=='-') f=-1;
    for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
    return x*f;

int que[xx][3];
int q[xx],all,ans[xx];
inline void bfs()

    int head=0,tail=-1;
    que[++tail][0]=0;
    que[tail][1]=0;
    que[tail][2]=0;
    while(head<=tail)
    
        int w=que[head][0],u=que[head][1],v=que[head++][2];
        fur(i,w+1,all)
        
            if(!ans[u^q[i]])
            
                que[++tail][0]=i;
                que[tail][1]=u^q[i];
                ans[u^q[i]]=v+1;
                que[tail][2]=v+1;
            
        
    

signed main()

    int t=in;
    q[1]=1;
    fur(i,2,18) q[i]=q[i-1]<<1;
    while(t--)
    
        int n=in,m=in;all=18;
        fur(i,1,n) q[++all]=in;
        int res=0;
        memset(ans,0,sizeof(ans));
        bfs();
        ans[0]=0;
        fur(i,1,m)
        
            int s=in,tt=in;
            res=(res+i*ans[s^tt]%mod)%mod;
        
        printf("%lld\n",res);
    
    return 0;

prob2:B

题目大意:给定一串字符串(仅存在英文26个字母),求最短的中字典序最小的非子序列字符串

开始以为是字符串题,后来以为是图论题,怎么都没想到是贪心+\(DP\)题(学姐狠啊),当字符串大小小于26时,可以很清楚得到答案,通过此性质从后往前\(DP\)

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define in read()
#define fur(i,a,b) for(int i=a;i<=b;++i)
#define fdr(i,a,b) for(int i=a;i>=b;--i)
#define xx 500010
#define inf 0x7fffffff
int f[xx][27],g[xx],pos[xx],n;
char ch[xx];
inline void dfs(int q,int len)

    if(len==1)
    
        fur(i,1,26)
        
            if(f[q][i]==n+1)
            
                printf("%c",i+'a'-1);
                return;
            
        
    
    fur(i,1,26)
    
        if(g[f[q][i]]==len)
        
            printf("%c",i+'a'-1);
            dfs(f[q][i]+1,len-1);
            break;
        
    

int main()

    scanf("%s",ch+1);
    n=strlen(ch+1);
    fur(i,1,26) f[n+1][i]=n+1,pos[i]=n+1;
    g[n+1]=1;
    fdr(i,n,1)
    
        fur(j,1,26) f[i][j]=f[i+1][j];
        f[i][ch[i]-'a'+1]=i;
        g[i]=inf;
        fur(j,1,26) g[i]=min(g[i],g[pos[j]]+1);
        pos[ch[i]-'a'+1]=i;
    
    int ans=inf;
    fur(i,1,26)
    
        if(f[1][i]==n+1)
        
            printf("%c\n",i+'a'-1);
            return 0;
        
        ans=min(ans,g[f[1][i]]);
    
    dfs(1,ans);
    printf("\n");
    return 0;

prb3:C

题目大意:给定\(n*m\)矩阵,求三点对三点共线的方案数

考试时式子推错,不想说什么了

#include<iostream>
#include<cmath>
#include<cstdio>
using namespace std;
#define in read()
#define fur(i,a,b) for(int i=a;i<=b;++i)
#define int long long
const int xx=1e7+10;
const int n6=166666668;
const int mod=1e9+7;
int phi[xx],prime[xx],cnt=0,ans=0,m,n;
bool mark[xx];
inline void handle()

    phi[1]=1;
    fur(i,2,n)
    
        if(!mark[i]) phi[i]=i-1,prime[++cnt]=i;
        for(int j=1;j<=cnt&&prime[j]*i<=n;++j)
        
            mark[i*prime[j]]=true;
            if(i%prime[j]) phi[i*prime[j]]=phi[i]*(prime[j]-1);
            else
            
                phi[i*prime[j]]=phi[i]*prime[j];
                break;
            
        
    

inline int calc(int x,int d,int u)return (x-u*d+x-d)*u/2%mod;
inline int C(int nm)return nm*(nm-1)%mod*(nm-2)%mod*n6%mod;
signed main()

    scanf("%lld%lld",&n,&m);
    if(n>m) swap(n,m);
    --n;--m;
    handle();
    fur(i,1,n)
    
        int tmp=phi[i];
        tmp=(tmp*calc(n+1,i,n/i))%mod;
        tmp=(tmp*calc(m+1,i,m/i))%mod;
        ans=(ans+tmp)%mod;
    
    ans=(ans-((n+1)*n/2%mod)*((m+1)*m/2%mod)%mod+mod)%mod;
    ans=(ans<<1)%mod;
    ans=(ans+(n+1)*C(m+1)%mod+(m+1)*C(n+1)%mod)%mod;
    printf("%lld\n",ans);
    return 0;

prob4:D

题目大意:给定\(n\)个标号结点所组成的任意图,求图中存在树的个数的\(k\)次方的和

知识储备:第二类斯特林数+prufer序列

而且上课心情不好,也没有认真听讲,导致这题不会,标程也没看懂,直接\(kuai\)学姐\(std\)算了。

(这压行真恶心):

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#define LL long long
#define N 50005
using namespace std;

const int mo=998244353,G=3;
int n,K,rev[N*3],len,num,g[25][N];
LL S[25][25],jc[N],ny[N],wn[50],f[N*3],h[N*3],ans;

LL qp(LL x,LL y) 
    LL res=1;
    for(;y;y>>=1ll,x=x*x%mo)
        if(y&1ll) res=res*x%mo;
    return res;

void pre() 
    jc[0]=ny[0]=1;
    for(int i=1;i<=n;++i) jc[i]=jc[i-1]*i%mo;
    ny[n]=qp(jc[n],mo-2);
    for(int i=n-1;i>=0;--i) ny[i]=ny[i+1]*(i+1)%mo;
    f[1]=1*ny[0];
    for(int i=2;i<=n;++i) f[i]=qp(i,i-2)*ny[i-1]%mo;
    S[0][0]=1;
    for(int i=1;i<=K;++i)
        for(int j=1;j<=i;++j)
            S[i][j]=(S[i-1][j]*j%mo+S[i-1][j-1])%mo;
    for(len=1,num=0;len<=n+n;len<<=1,num++);
    for(int i=1;i<len;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(num-1));
    for(int i=0;i<=45;++i) wn[i]=qp(G,(mo-1)/(1<<i));

void ntt(LL *x,int sign) 
    for(int i=0;i<len;++i) if(i<rev[i]) swap(x[i],x[rev[i]]);
    for(int k=2,e=1;k<=len;k<<=1,e++) 
        for(int i=0;i<len;i+=k) 
            LL *a=x+i,*b=x+i+(k>>1); LL u,v,w=1;
            for(int j=0;j<(k>>1);++j,w=w*wn[e]%mo) 
                u=a[j],v=b[j]*w%mo;
                a[j]=u+v; a[j]>=mo?a[j]-=mo:0;
                b[j]=u-v; b[j]<0?b[j]+=mo:0;
            
        
    
    if(sign==-1) 
        int inv=qp(len,mo-2);
        for(int i=0;i<len;++i) x[i]=x[i]*inv%mo;
        reverse(x+1,x+len);
    

LL C(LL x,LL y) return x>=y?jc[x]*ny[y]%mo*ny[x-y]%mo:0;
int main()

    scanf("%d%d",&n,&K);
    pre();
    g[0][0]=1; ntt(f,1);
    for(int i=1;i<=K;++i) 
        memset(h,0,sizeof(h[0])*len);
        for(int j=0;j<=n;++j) h[j]=1ll*g[i-1][j]*ny[j]%mo;
        ntt(h,1);
        for(int j=0;j<len;++j) h[j]=h[j]*f[j]%mo;
        ntt(h,-1);
        for(int j=1;j<=n;++j) g[i][j]=h[j]*jc[j-1]%mo;
    
    for(int i=1;i<=n;++i) 
        LL tmp=qp(2,1ll*(n-i)*(n-i-1)/2ll)*C(n,i)%mo;
        for(int j=1;j<=K;++j) 
            ans+=g[j][i]*S[K][j]%mo*jc[j]%mo*tmp%mo;     //!!!!!
            ans>=mo?ans-=mo:0;
        
    
    printf("%lld\n",ans);
    return 0;

以上是关于2019年7月26日(数学DP)的主要内容,如果未能解决你的问题,请参考以下文章

2019年9月14日(数论专题考试)

《天天数学》连载42:二月十一日

《天天数学》连载42:二月十一日

3.14圆周率节,这5本书带你领略数学的魅力

2018年数学建模 国赛时间

2023年度数学建模竞赛汇总