3.20省选练习(下午)
T1
//显然的 //直接求的话肯定不太行 //递推式如下 f[n]=(x+y)f[n-1]-xyf[n-2] #include<bits/stdc++.h> #define mod 998244353 #define int long long using namespace std; struct node { int jz[3][3]; void Init() { memset(jz,0,sizeof(jz)); } }zy,Ans; node mul(node A,node B) { node res; res.Init(); for(int i=1;i<=2;i++) { for(int j=1;j<=2;j++) { for(int k=1;k<=2;k++) { res.jz[i][j]+=A.jz[i][k]*B.jz[k][j]; res.jz[i][j]%=mod; } } } return res; } int sum,ji,n; signed main() { scanf("%lld%lld%lld",&sum,&ji,&n); Ans.Init(); zy.Init(); Ans.jz[1][1]=2; Ans.jz[1][2]=sum; zy.jz[2][1]=1; zy.jz[1][2]=-ji; zy.jz[2][2]=sum; while(n) { if(n&1) { Ans=mul(Ans,zy); } zy=mul(zy,zy); n>>=1; } cout<<Ans.jz[1][1]; }
T2
//上午想了个区间dp,发现貌似不太能转移 //位置i最后高度是h[poz[i]],显然poz[i]不降,如果降的话 //显然的,如果出现了前面的,那么中间一段都应该是那个更低的 //不然的话,都是前面那个 //那么既然是单调的,那么可以枚举断点来转移了 //而且poz[i]是poz[i]-i的之间的最小值 //那么对于序列b进行dp就好了,复杂度为O(n^2) //dp[i][j]表示考虑最终局面i-n这个后缀,是由初始j-n后缀的方案数 //倒推就好了,dp[i][j]=dp[i+1][j-n] //而且我们第i个显然时第j个位置的值,那么i-j这段区间最小值必然是j #include<bits/stdc++.h> #define mod 1000000007 #define int long long #define MAXN 3030 using namespace std; int a[MAXN],dp[MAXN][MAXN],sum[MAXN][MAXN]; int Min[MAXN],n; signed main() { //大概就是求序列最终方案数 cin>>n; for(int i=1;i<=n;i++) { cin>>a[i]; } for(int i=n;i>=1;i--) { Min[i]=a[i]; for(int j=i+1;j<=n;j++) Min[j]=min(Min[j-1],a[j]); for(int j=i-1;j>=1;j--) Min[j]=min(Min[j+1],a[j]); for(int j=1;j<=n;j++) { // cout<<a[j]<<" "<<Min[j]<<endl; if(Min[j]==a[j]) { if(i==n) { dp[i][j]=1; continue; } dp[i][j]=sum[i+1][j]; } // cout<<Min[j]<<" "<<a[j]<<" "<<dp[i][j]<<" "; } // for(int j=1;j<=n;j++) cout<<dp[i][j]<<" "; // cout<<endl; for(int j=n;j>=1;j--) { sum[i][j]=sum[i][j+1]+dp[i][j]; sum[i][j]%=mod; } } cout<<(sum[1][1]%=mod); }
T3
//一维容斥的话很常见 //就是枚举多少个不合法的然后+-计算就好了 //那么其实扩展到高维也很好说 //无非是我们一开始一维的容斥保证了第二维是合法的 //那么我们对于每一个行的选择,只需要乘上当前行状态下满足的列的容斥结果就好了 //就是先对第一维容斥展开后对第二维容斥继续展开的过程 //最后的式子大概就是 //枚举一个行集合,枚举一个列集合,然后这些集合不合法,其余乱放,加上容斥系数就好了 //容斥系数也很好说,就是每一维度的乘积就好了 //暴力就很好写了,直接计算出所有最终状态 //然后暴力容斥出每种情况的不合法就好了 //我还是先去做做低维容斥吧