BZOJ2019 题解

BZOJ2019 题解


在更了在更了

P5319 [BJOI2019]奥术神杖

\(V_i\)求个\(\ln\)变成了让平均数最大,显然套分数规划,然后ac自动机上面dp

#include<bits/stdc++.h>
#define il inline
#define vd void
typedef long long ll;
il ll gi(){
    ll x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return x*f;
}
char T[1510],S[1510];
int ch[1510][10],trans[1510][10],fail[1510],cnt;
double W[1510];int sum[1510];
il vd insert(double d){
    int n=strlen(S+1);
    int x=0;
    for(int i=1;i<=n;++i){
        S[i]-='0';
        if(!ch[x][S[i]])ch[x][S[i]]=++cnt;
        x=ch[x][S[i]];
    }
    W[x]+=d;++sum[x];
}
int que[1510],hd,tl;
double f[1510][1510];
int g[1510][1510];
char h[1510][1510];
template<class T> il vd chkmx(T&a,T b){if(b>a)a=b;}
int main(){
#ifdef XZZSB
    freopen("in.in","r",stdin);
    freopen("out.out","w",stdout);
#endif
    int n=gi(),m=gi();
    scanf("%s",T+1);
    for(int i=1;i<=m;++i)scanf("%s",S+1),insert(log(gi()));
    for(int i=0;i<10;++i)if(ch[0][i])trans[0][i]=ch[0][i],que[tl++]=ch[0][i];
    while(hd^tl){
        int x=que[hd++];
        for(int i=0;i<10;++i)
            if(ch[x][i]){
                int f=fail[x];
                while(f&&!ch[f][i])f=fail[f];
                fail[ch[x][i]]=ch[f][i];
                que[tl++]=ch[x][i];
                W[ch[x][i]]+=W[ch[f][i]];
                sum[ch[x][i]]+=sum[ch[f][i]];
                trans[x][i]=ch[x][i];
            }else trans[x][i]=trans[fail[x]][i];
    }
    double l=0,r=1e9,mid;
    while(r-l>1e-4){
        mid=(l+r)*0.5;
        for(int i=0;i<=cnt;++i)f[0][i]=-1e18;
        f[0][0]=0;
        for(int i=0;i<n;++i){
            for(int j=0;j<=cnt;++j)f[i+1][j]=-1e18;
            for(int j=0;j<=cnt;++j){
                if(f[i][j]<-1e17)continue;
                for(int k=0;k<10;++k)if(T[i+1]=='.'||k+'0'==T[i+1])chkmx(f[i+1][trans[j][k]],f[i][j]+W[trans[j][k]]-mid*sum[trans[j][k]]);
            }
        }
        bool flg=0;
        for(int i=0;i<=cnt;++i)if(f[n][i]>1e-7)flg=1;
        if(flg)l=mid;
        else r=mid;
    }
    for(int i=0;i<=cnt;++i)f[0][i]=-1e18;
    f[0][0]=0;
    for(int i=0;i<n;++i){
        for(int j=0;j<=cnt;++j)f[i+1][j]=-1e18;
        for(int j=0;j<=cnt;++j){
            if(f[i][j]<-1e17)continue;
            for(int k=0;k<10;++k)
                if(T[i+1]=='.'||k+'0'==T[i+1])
                if(f[i+1][trans[j][k]]<f[i][j]+W[trans[j][k]]-l*sum[trans[j][k]]){
                    f[i+1][trans[j][k]]=f[i][j]+W[trans[j][k]]-l*sum[trans[j][k]];
                    g[i+1][trans[j][k]]=j;
                    h[i+1][trans[j][k]]='0'+k;
                }
        }
    }
    double F=1e-18;int G=0;
    for(int i=0;i<=cnt;++i)if(f[n][i]>F)F=f[n][i],G=i;
    for(int i=n;i;--i)T[i]=h[i][G],G=g[i][G];
    printf("%s",T+1);
    return 0;
}

P5320 [BJOI2019]勘破神机

神鸡???

这是一个强行二合一,但这两个题还是有关系的

先看\(ans2\) 化一下式子就可以知道答案和\(\sum_{i=0}^n\binom{fib_i}k\)有关。

再看看\(ans3\),显然\(n\)为奇数是没有答案,设\(f_n\)表示\(2n\)列时的答案。

考虑怎么放。首先有一种放法就是三个横条,从\(f_{n-1}\)转移过来,1种放法;还有一种方法就是

---------
| |   | |
- ----- -
| |   | |
---------
|   |   |
---------

这样可以放任意长度为偶数的段,而且上下翻转也是一种方案,所以从任意的\(f_{i}(i<n)\)可以转移过来,有2种方法。

综上,\(f_n=f_{n-1}+2\sum_{i=0}^{n-1}f_i\)

\(g_n=\sum_{i=0}^nf_n\),那么用上面的递推式改改可以得到另一个递推式\(g_n=4g_{n-1}-g_{n-2}\)

下面\(ans2\)\(ans3\)的方法是类似的。

\(\binom nk\)显然是个\(k\)次多项式,可以\(O(k^2)\)的时间预处理出来

那么答案变成了

\(\sum_{i=0}^n\sum_{j=0}^ka_jfib_i^j\)

\(\sum_{j=0}^ka_j\sum_{i=0}^nfib_i^j\)

那么问题变成了对每一个\(k\)计算\(\sum_{i=0}^nfib_i^k\)

考虑\(fib\)的通项公式,我们知道了是\(f(n)=\frac{(\frac{1+\sqrt5}2)^n-(\frac{1-\sqrt5}2)^n}{\sqrt5}\)

再看看\(ans3\)的通项公式,可以解出来\(f(n)=\frac{(3+\sqrt3)(2+\sqrt3)^n+(3-\sqrt3)(2-\sqrt3)^n}{6}\)

统一写成\(f(x)=ab^i+cd^i\)

\(\sum_{i=0}^n(ab^i+cd^i)^k\)

用二项式定理直接展开,\(\sum_{i=0}^n\sum_{j=0}^k\binom kj (ab^i)^j(cd^i)^{k-j}\)

\(\sum_{i=0}^n\sum_{j=0}^k\binom kj a^jb^{ij}c^{k-j}d^{i(k-j)}\)

\(\sum_{i=0}^nf(i)^k=\sum_{j=0}^k\binom kj a^jc^{k-j}\sum_{i=0}^n(b^{j}d^{k-j})^i\)

后面直接等比数列求和即可,那么这个式子就能算了。

但是需要注意, \(3\)\(5\)\(\mod 998244353\)意义下都没有二次剩余,可以开一个struct存\(a,b\),真实数就是\(a+b\sqrt 5\)

\(F_x\)通项的方法:不想写了,贴个链接https://blog.csdn.net/liuzibujian/article/details/82595918

#include<bits/stdc++.h>
#define il inline
#define vd void
#define mod 998244353
#define ll long long
il ll gi(){
    ll x=0,f=0;char ch=getchar();
    while(!isdigit(ch))f^=ch=='-',ch=getchar();
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return f?-x:x;
}
il int pow(int a,int b){
    int ret=1;
    while(b){
        if(b&1)ret=1ll*ret*a%mod;
        a=1ll*a*a%mod;b>>=1;
    }
    return ret;
}
ll k,l,r,qt,m;
struct number{
    int a,b;
    number inv()const{
        int fm=pow((1ll*a*a-qt*b*b%mod+mod)%mod,mod-2);
        return (number){1ll*a*fm%mod,(mod-1ll*b*fm%mod)%mod};
    }
};//a+b*sqrt(qt)
il number getnum(int x){return(number){x,0};}
il number operator+(const number&a,const number&b){return(number){(a.a+b.a)%mod,(a.b+b.b)%mod};}
il number operator-(const number&a,const number&b){return(number){(a.a-b.a+mod)%mod,(a.b-b.b+mod)%mod};}
il number operator*(const number&a,const number&b){return(number){(1ll*a.a*b.a+qt*a.b*b.b)%mod,(1ll*a.a*b.b+1ll*a.b*b.a)%mod};}
il number operator/(const number&a,const number&b){return a*b.inv();}
il number Pow(number a,ll b){
    number ret=getnum(1);
    while(b){
        if(b&1)ret=ret*a;
        a=a*a;b>>=1;
    }
    return ret;
}
int A[510],B[510],C[510][510];
number pa[510],pb[510],pc[510],pd[510];
il ll solve(ll n){
    ll ans=0;
    for(int i=0;i<=k;++i){
        ll res=0;
        for(int o=0;o<=i;++o){
            number _res=getnum(C[i][o]);
            number p=pb[o]*pd[i-o];
            if(p.a==1&&p.b==0)_res=_res*getnum(n%mod);
            else _res=_res*(Pow(p,n+1)-getnum(1))/(p-getnum(1));
            res=(res+(_res*pa[o]*pc[i-o]).a)%mod;
        }
        ans=(ans+res*A[i])%mod;
    }
    return ans;
}
il vd init(){
    if(m==2)qt=5;
    else qt=3;
    C[0][0]=1;
    for(int i=1;i<=501;++i){
        C[i][0]=1;
        for(int j=1;j<=i;++j)C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
    }
    pa[0]=pb[0]=pc[0]=pd[0]=getnum(1);
    if(m==2){
        pa[1]=(number){1,0}/(number){0,1};
        pb[1]=(number){1,1}/getnum(2);
        pc[1]=(number){mod-1,0}/(number){0,1};
        pd[1]=(number){1,mod-1}/getnum(2);
    }else{
        pa[1]=(number){3,1}/getnum(6);
        pb[1]=(number){2,1};
        pc[1]=(number){3,mod-1}/getnum(6);
        pd[1]=(number){2,mod-1};
    }
    for(int i=2;i<=501;++i)pa[i]=pa[i-1]*pa[1],pb[i]=pb[i-1]*pb[1],pc[i]=pc[i-1]*pc[1],pd[i]=pd[i-1]*pd[1];
}
il vd work(){
    memset(A,0,sizeof A);A[0]=1;
    for(int i=0;i<k;++i){
        memcpy(B,A,sizeof A);
        memset(A,0,sizeof A);
        for(int j=0;j<=k;++j)A[j]=mod-1ll*B[j]*i%mod;
        for(int j=0;j<=k;++j)A[j+1]=(A[j+1]+B[j])%mod;
    }
    ll _l,_r;
    if(m==3)_l=(l+1)/2-1,_r=r/2-1;
    else _l=l,_r=r;
    ll ans=(solve(_r+1)-solve(_l)+mod)%mod;
    for(int i=1;i<=k;++i)ans=ans*pow(i,mod-2)%mod;
    printf("%lld\n",ans*pow((r-l+1)%mod,mod-2)%mod);
}
int main(){
#ifdef XZZSB
    freopen("in.in","r",stdin);
    freopen("out.out","w",stdout);
#endif
    int T=gi();m=gi();
    init();
    while(T--){
        l=gi(),r=gi(),k=gi();
        work();
    }
    return 0;
}

P5322 [BJOI2019] 排兵布阵

直接dp。

#include<bits/stdc++.h>
#define il inline
#define vd void
typedef long long ll;
il ll gi(){
    ll x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int f[101][20001],a[101][101];
int main(){
#ifdef XZZSB
    freopen("in.in","r",stdin);
    freopen("out.out","w",stdout);
#endif
    int s=gi(),n=gi(),m=gi();
    for(int i=1;i<=s;++i)
        for(int j=1;j<=n;++j)
            a[j][i]=gi()*2+1;
    for(int i=1;i<=n;++i){
        std::sort(a[i]+1,a[i]+s+1);
        for(int j=0;j<=m;++j)
            for(int k=0;k<=s;++k)
                if(j+a[i][k]<=m)f[i][j+a[i][k]]=std::max(f[i][j+a[i][k]],f[i-1][j]+i*k);
                else break;
    }
    printf("%d\n",f[n][m]);
    return 0;
}

P5323 [BJOI2019] 光线

几块玻璃可以合起来,这块玻璃有从上到下/从下到上的透光度/反射度。

每次合并\(1-i\)的玻璃和\(i+1\)玻璃,发现上面这个玻璃只要记上到下的透光度和下到上的反射度就行了。

新玻璃透光度和反射度的式子手推就行了,大约是一个等比数列。

#include<bits/stdc++.h>
#define il inline
#define vd void
#define mod 1000000007
typedef long long ll;
il ll gi(){
    ll x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return x*f;
}
il vd exgcd(int a,int b,int&x,int&y){
    if(!b)x=1,y=0;
    else exgcd(b,a%b,y,x),y-=x*(a/b);
}
il int inv(int o){
    int a=o,b=mod,x,y;
    exgcd(a,b,x,y);
    return (x+mod)%mod;
}
int main(){
#ifdef XZZSB
    freopen("in.in","r",stdin);
    freopen("out.out","w",stdout);
#endif
    int n=gi();
    int a1=gi()*570000004ll%mod,b1=gi()*570000004ll%mod;
    for(int i=2;i<=n;++i){
        int a2=gi()*570000004ll%mod,b2=gi()*570000004ll%mod;
        int iv=inv((mod+1-1ll*b1*b2%mod)%mod);
        int a3=1ll*a1*a2%mod*iv%mod;
        int b3=(b2+1ll*a2*a2%mod*b1%mod*iv%mod)%mod;
        a1=a3,b1=b3;
    }
    printf("%d\n",a1);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/xzz_233/p/10756737.html