zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    第十二章学习笔记
    UVa OJ 107 The Cat in the Hat (戴帽子的猫)
    UVa OJ 123 Searching Quickly (快速查找)
    UVa OJ 119 Greedy Gift Givers (贪婪的送礼者)
    UVa OJ 113 Power of Cryptography (密文的乘方)
    UVa OJ 112 Tree Summing (树的求和)
    UVa OJ 641 Do the Untwist (解密工作)
    UVa OJ 105 The Skyline Problem (地平线问题)
    UVa OJ 100 The 3n + 1 problem (3n + 1问题)
    UVa OJ 121 Pipe Fitters (装管子)
  • 原文地址:https://www.cnblogs.com/xzz_233/p/10756737.html
Copyright © 2011-2022 走看看