• BJOI2019 题解


    BJOI2019 题解


    在更了在更了

    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}^ninom{fib_i}k)有关。

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

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

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

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

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

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

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

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

    那么答案变成了

    (sum_{i=0}^nsum_{j=0}^ka_jfib_i^j)

    (sum_{j=0}^ka_jsum_{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}^nsum_{j=0}^kinom kj (ab^i)^j(cd^i)^{k-j})

    (sum_{i=0}^nsum_{j=0}^kinom kj a^jb^{ij}c^{k-j}d^{i(k-j)})

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

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

    但是需要注意, (3)(5)(mod 998244353)意义下都没有二次剩余,可以开一个struct存(a,b),真实数就是(a+bsqrt 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
    ",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;
    }
    

    P5321 [BJOI2019]送别

    等zsy更了我再更。

    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
    ",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
    ",a1);
        return 0;
    }
    

    P5324 [BJOI2019] 删数

    有一个简单的dp,(f[i])表示现在长度为(i),每次删(j)(i)并跳到(i-j),如果没有一个(i)直接跳到(i-1),这样跳到(0)最多删掉多少数。

    然后有一个差不多的问题:设数(i)(cnt_i)个,就覆盖([i-cnt_i+1,i]),求最后多少个数没有被覆盖。感性理解感性证明这个的答案和上面的(dp)一样。

    那么就是一个普及题了:用线段树维护,每次修改两个(cnt)或者移动区间(都可以变成区间加),查询线段树上一段(0)的数量。移动区间可以线段树两边都扩展(m)

    查询线段树上一段(0)的数量,我以为要记是0的数量,就做不了了。实际上不会有数减到(-1),所以可以记最小值的数量,就能做了。

    #include<bits/stdc++.h>
    #define il inline
    #define vd void
    typedef long long ll;
    il int gi(){
    	int x=0,f=1;
    	char ch=getchar();
    	while(!isdigit(ch))f^=ch=='-',ch=getchar();
    	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    	return x*f;
    }
    int n,m,N,L,a[450002],cnt[450002];
    #define mid ((l+r)>>1)
    int lz[1800010];
    std::pair<int,int>s[1800010];
    il std::pair<int,int>operator+(std::pair<int,int>a,std::pair<int,int>b){
    	if(a.first<b.first)return a;
    	else if(a.first>b.first)return b;
    	else return{a.first,a.second+b.second};
    }
    il vd build(int x,int l,int r){
    	s[x].second=r-l+1;if(l==r)return;
    	build(x<<1,l,mid),build(x<<1|1,mid+1,r);
    }
    il vd upd(int x,int y){s[x].first+=y;lz[x]+=y;}
    il vd down(int x){if(lz[x])upd(x<<1,lz[x]),upd(x<<1|1,lz[x]),lz[x]=0;}
    il vd update(int x,int l,int r,const int&L,const int&R,const int&d){
    	if(L<=l&&r<=R)return upd(x,d);
    	down(x);
    	if(L<=mid)update(x<<1,l,mid,L,R,d);
    	if(mid<R)update(x<<1|1,mid+1,r,L,R,d);
    	s[x]=s[x<<1]+s[x<<1|1];
    }
    il std::pair<int,int>query(int x,int l,int r,const int&L,const int&R){
    	if(L<=l&&r<=R)return s[x];
    	down(x);
    	if(L<=mid)
    		if(mid<R)return query(x<<1,l,mid,L,R)+query(x<<1|1,mid+1,r,L,R);
    		else return query(x<<1,l,mid,L,R);
    	else return query(x<<1|1,mid+1,r,L,R);
    }
    il vd update(int p,int x){
    	if(x==1){
    		if(1<=p-cnt[p]&&p-cnt[p]<=N&&L+1<=p&&p<=L+n)update(1,1,N,p-cnt[p],p-cnt[p],1);
    		++cnt[p];
    	}else{
    		if(1<=p-cnt[p]+1&&p-cnt[p]+1<=N&&L+1<=p&&p<=L+n)update(1,1,N,p-cnt[p]+1,p-cnt[p]+1,-1);
    		--cnt[p];
    	}
    }
    #undef mid
    int main(){
    	n=gi(),m=gi(),N=n+m*2+2,L=m+1;
    	int p,x;
    	build(1,1,N);
    	for(int i=1;i<=n;++i)update(a[i]=gi()+L,1);
    	for(int i=1;i<=m;++i){
    		p=gi(),x=gi();
    		if(p>0)update(a[p],-1),update(a[p]=x+L,1);
    		else
    			if(x==1)update(1,1,N,L+n-cnt[L+n]+1,L+n,-1),--L,update(1,1,N,L+1-cnt[L+1]+1,L+1,1);
    			else update(1,1,N,L+1-cnt[L+1]+1,L+1,-1),++L,update(1,1,N,L+n-cnt[L+n]+1,L+n,1);
    		auto ans=query(1,1,N,L+1,L+n);
    		printf("%d
    ",ans.first?0:ans.second);
    	}
    	return 0;
    }
    
  • 相关阅读:
    Vue3小知识
    Eslint小知识
    微信小程序注意点
    vue常用方法2
    vue常用方法
    vue组件常用方法
    013 --TypeScript之高级类型
    012--TypeScript之类型推断
    jenkins window unity 控制台输出中文乱码
    Unity 生成 Android App Bundle(aab) (二)
  • 原文地址:https://www.cnblogs.com/xzz_233/p/10756737.html
Copyright © 2020-2023  润新知