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

//一维容斥的话很常见 
//就是枚举多少个不合法的然后+-计算就好了
//那么其实扩展到高维也很好说
//无非是我们一开始一维的容斥保证了第二维是合法的
//那么我们对于每一个行的选择,只需要乘上当前行状态下满足的列的容斥结果就好了
//就是先对第一维容斥展开后对第二维容斥继续展开的过程
//最后的式子大概就是
//枚举一个行集合,枚举一个列集合,然后这些集合不合法,其余乱放,加上容斥系数就好了
//容斥系数也很好说,就是每一维度的乘积就好了
//暴力就很好写了,直接计算出所有最终状态
//然后暴力容斥出每种情况的不合法就好了
//我还是先去做做低维容斥吧 

 

posted @ 2022-03-20 19:46  Authentic_k  阅读(21)  评论(0编辑  收藏  举报