【题解】牛客寒假集训营6 BEF
回文括号序列计数
https://ac.nowcoder.com/acm/contest/9986/A
别的题没什么好说的,H正解的线段树我不会,用暴力卡过去了(数据不强)
主要说下BEF三题吧:
B
正解的思路是把转化为
我没想到这个方法,而是用打表找规律来做的:
可以发现,当n=4、13、40。。。的时候,全部都是1。而
所以首先根据 所在那一块进行分类,然后再根据更细的分类进行讨论。这里就不写细节了,毕竟推了快2小时。。
给大家介绍一下我找规律和debug的技巧。首先一定要写一个对拍函数,以及一个check函数,这样可以快速得出自己算出来的对不对。然后是不要一口气全写完,可以写一部分对拍一次,查看这一部分写的对不对,这样debug起来不容易出错。
#include<bits/stdc++.h> using namespace std; #define ll long long ll a[1000][1000],b[1000][1000]; ll kk; ll f(ll n,ll m){ // cout<<n<<" "<<m<<endl; if(m>2*n)return 0; if(n<=kk)return a[n][m]; ll p=1,sum=0; while(sum<n)sum+=p,p*=3; p/=3; ll l=sum-p,r=sum,mid=l+r+1>>1; ll dis=n-l; if(n==l&&n==r)return 1; // cout<<n<<" "<<m<<" "<<dis<<endl; if(n>=mid&&n<=r)return f(n-p,m%p); else{ // cout<<p<<endl; if(m<=p-1&&m>=dis*2)return 0; if(m>n)return f(n,2*n-m); if(m>=dis)return (3-f(n,2*dis-1-m))%3; ll mid2=l+p/3; // cout<<mid2<<endl; if(n>mid2)return f(n-p/3*2,m%(p/3)); // if(n==mid2)return 1; ll mid3=l+(p/3-(sum-p-p/3)); // cout<<mid2<<endl; if(n>mid3)return f(n-p/3,m); else{ // cout<<n<<" "<<m<<" "<<2*dis<<endl; if(m>=2*dis)return 0; return f(n-p/3,m); } } } int main(){ ll n,i,j,m,_=1; // cin>>_; a[0][0]=1; a[1][0]=a[1][1]=a[1][2]=1; for(i=2;i<300;i++){ for(j=0;j<500;j++){ a[i][j]+=a[i-1][j]; a[i][j+1]+=a[i-1][j]; a[i][j+2]+=a[i-1][j]; } for(j=0;j<500;j++)a[i][j]%=3; } // cout<<f(22,9)<<endl; kk=13; for(i=0;i<40;i++){ // cout<<i<<": "; for(j=0;j<2*i+2;j++){ // b[i][j]=f(i,j); // cout<<f(i,j)<<" "; } // cout<<endl; } kk=40; for(i=0;i<40;i++){ for(j=0;j<2*i+2;j++){ // if(b[i][j]!=f(i,j)){cout<<i<<" "<<j<<" "<<-1;return 0;} } } int t; cin>>t; while(t--){ cin>>n>>m; cout<<f(n,m)<<endl; } }
E
我们先观察第一行。
第一行第一个只能选择右和下。第一行最后一个只能选择左和下。其他的可以选择左/右和下。
那么很明显,这一行带来的权值最大的贡献(不考虑和下面的)可以用dp来解决,对应每个数选择向左/向右为 和 ,就很容易解决了。
其他的行同理,列也同理。
#include<bits/stdc++.h> using namespace std; #define ll long long ll dp[1010][2],a[1010][1010]; int gao(int x,int y){ int p=x^y; int i=p,cnt=0; while(i)cnt+=i%2,i/=2; return p+cnt; } int main(){ ll n,i,j=0,k,z=0,sum=0,cnt=0; ll m; cin>>n>>m; for(i=0;i<n;i++){ for(j=0;j<m;j++){ cin>>a[i][j]; } } ll ans=0; for(i=0;i<n;i++){ for(j=0;j<m;j++)dp[j][0]=dp[j][1]=0; dp[1][1]=gao(a[i][0],a[i][1]); for(j=2;j<m;j++){ dp[j][0]=max(dp[j-1][0],dp[j-1][1]); dp[j][1]=max(dp[j-2][0],dp[j-2][1])+gao(a[i][j],a[i][j-1]); } // cout<<max(dp[m-1][0],dp[m-1][1])<<endl; ans+=max(dp[m-1][0],dp[m-1][1]); } for(j=0;j<m;j++){ for(i=0;i<n;i++)dp[i][0]=dp[i][1]=0; dp[1][1]=gao(a[0][j],a[1][j]); for(i=2;i<n;i++){ dp[i][0]=max(dp[i-1][0],dp[i-1][1]); dp[i][1]=max(dp[i-2][0],dp[i-2][1])+gao(a[i][j],a[i-1][j]); // cout<<i<<" "<<j<<" "<<dp[i][0]<<" "<<dp[i][1]<<endl; } // cout<<max(dp[n-1][0],dp[n-1][1])<<endl; ans+=max(dp[n-1][0],dp[n-1][1]); } cout<<ans; }
F
这道题有很多人是OEIS过的,我说一下我的推导过程。
首先要明确两个公式:
(这个公式可以用上一个公式推出)
然后推导的时候为了方便,可以拿具体的数为例子(本题用12进行推导)。
设
那么有:
由于上述推导过程适用于任何正整数 不仅仅是12,所以可以得出递推式:
用这个递推式继续化简:
………………①式
………………②式
………………③式
……………………
其中,假设已知f(4),那么可以用①式推出f(8),②式推出f(12)。。。以此类推可以推出所有的f(n)了
#include<bits/stdc++.h> using namespace std; #define ll long long string s[1010]; ll dp[100010]; int mod=998244353; ll power(ll a,ll b){ ll res=1; while(b){ if(b&1)res=res*a%mod; b>>=1; a=a*a%mod; } return res; } int main(){ ll n,i,j,k,t,m,sum=0; //2^(n-2)-或+2*(n/2)+ cin>>n; cout<<((power(2,n-2)+(n%8!=0?-1:1)*power(2,n/2)-(n%8!=0?-1:1)*power(2,n/2-1))%mod+mod)%mod; }