W round 1 题解

W round 1 题解

比赛链接 https://www.luogu.org/contest/18397

.好题

  • 题目意思

    给一个巨大的杨辉三角,采用类似入门题(数字三角形)的方式求从顶点到指定点的最小累加和。

  • 输出样例,和样例中的第二组数据一样呀

  • 应该比较好写吧

  • 组合数问题

顶点到指定点的最小累加和,一定是走边上。因为边上的数值最小。从顶点到指定点的最小累加和,是先走步竖直向下的,每步取最小值,然后再斜向下走一直到.累加和为。另外,我们可以发现,当时,先斜向下走到,再竖直向下走到,路径上的数值累加和是最小的。但是这道题目直接运用求组合数是会超时的,要加不少预处理才行。

  • 思维难度:左右
  • 代码难度:左右

标准程序

#include <bits/stdc++.h>
#define int long long
#define re register
using namespace std;
int n,m,p,res,Case,cnt,bin[2005][10090],pri[20005],zs[2001];

inline bool pd(int x) {
    if(x<2) return false;
    for ( re int i=2;i*i<=x;i++ ) 
        if(x%i==0) return false;
    return true;
}

inline void phi() {
    for ( re int i=2;i<=1e4;i++ ) 
        if(pd(i)) pri[i]=++cnt,zs[cnt]=i;
}

inline int ksm(int a,int b) {
    int ret=1;
    while(b) {
        if(b&1) ret=ret*a%p;
        a=a*a%p; b>>=1;
    }
    return ret%p;
}

inline void init() {
    for ( re int i=1;i<=cnt;i++ ) {
        bin[i][0]=1;
        for ( re int j=1;j<=10000;j++ ) 
            bin[i][j]=bin[i][j-1]*j%zs[i];
    }
}

inline int C(int n,int m) {
    if(n<m) return 0;
    int summ=ksm((bin[pri[p]][n-m]),p-2)%p;
    int a=((bin[pri[p]][n])*summ)%p;
    int b=bin[pri[p]][m];
    return a*ksm(b,p-2)%p;
}

inline int Lucas(int n,int m,int p) {
    if(!m) return 1;
    else return (C(n%p,m%p)*Lucas(n/p,m/p,p));
}

inline int read() {
    int sum=0,ff=1; char ch=getchar();
    while(ch<'0' or ch>'9') { if(ch=='-') ff=-1; ch=getchar(); }; 
    while(ch>='0' and ch<='9') { sum=sum*10+ch-'0'; ch=getchar(); }; 
    return sum*ff;
}

signed main() {
    phi(); init(); 
    int T=read();
    while(T--) {
        n=read(),m=read(),p=read();
        if(m>n/2) m=n-m;
        printf("%lld\n",(Lucas(n+1,m,p)+n-m+p)%p);
    }
    return 0;
}

模拟爆蛋题

这道题目的出题人是巨神,再这里

  • 数位DP

比较容易些吧。

  • 发现规律似乎是裴波那切数列

然后写一个矩乘,就可以啦

  • 可以参照这道题目

里面的题解有详细的证明,在这里不证明了(逃

  • 思维难度:
  • 代码难度:

    标准程序

    #include<bits/stdc++.h>
    #define LL long long
    using namespace std;
    const int N=1e4,S=30000001;
    char s[S];
    LL pi[N],k[N],P;
    inline LL gcd(register LL n,register LL m){while(m^=n^=m^=n%=m); return n;}
    inline LL lcm(register LL n,register LL m){return n/gcd(n,m)*m;}
    struct matrix
    {
      LL a[3][3];
      matrix(){memset(a,0,sizeof(a));}
      matrix operator*(matrix x)
      {
          matrix s;
          for(register int i=1;i<=2;i++) for(register int j=1;j<=2;j++) for(register int k=1;k<=2;k++) s.a[i][j]=(s.a[i][j]+a[i][k]*x.a[k][j])%P;
          return s;
      }
      matrix operator^(register LL k)
      {
          matrix s=*this,e;
          e.a[1][1]=e.a[2][2]=1;
      for(;k;k>>=1,s=s*s) if(k&1) e=e*s; 
          return e;
      }
    };
    inline int power(register LL n)
    {
      matrix a,ans;
      a.a[1][1]=a.a[1][2]=a.a[2][1]=1,a.a[2][2]=0;
      ans=a^n;
      return ans.a[1][2];
    }
    inline LL Get(register LL p)
    {
      register int s=sqrt(p),tot=0;
      for(register int i=2;i<=s;++i)
      if(!(p%i))
      {
          pi[++tot]=i,k[tot]=1;
          while(!(p%i)) p/=i,k[tot]*=i;
      }
      for(register int i=1;i<=tot;++i) k[i]/=pi[i];
      if(p!=1) k[++tot]=1,pi[tot]=p;
      for(register int i=1;i<=tot;++i)
          if(pi[i]==2) k[i]*=3;
          else if(pi[i]==3) k[i]*=5;
          else if(pi[i]==5) k[i]*=20;
          else if(pi[i]%5==1||pi[i]%5==4) k[i]*=pi[i]-1;
          else k[i]*=(pi[i]+1)<<1;
      register LL ans=k[1];
      for(register int i=2;i<=tot;++i) ans=lcm(ans,k[i]);
      return ans;
    }
    int main()
    {
      register int len;
      register LL m,n=0;
      scanf("%s%lld",s+1,&P),len=strlen(s+1);
      if(P==1) return cout<<0,0;
      m=Get(P);
      for(register int i=1;i<=len;++i) n=((n<<3)+(n<<1)+(s[i]^48))%m;
      if(!n) return cout<<0,0;
      if(n==1) return cout<<1,0;
      return cout<<power(n),0;    
    }

电梯

题目意思

就是有两种运输机,一种比较贵但是效率高,一种便宜但效率低,问你把所有物品运输完成的最小花费。

  • 错误贪心—大力贪心

  • DP

表示前面个物品,次电梯的最优解

表示在还是停留的花费少

先预处理出预处理出

于是开始大力,这个很显然

因为肯定是有转移过来,于是暴力转移即可

最后再O统计答案即可

总体复杂度大约为因为每次循环都是跑不满的,所以O(能过)

  • 思维难度:

  • 代码难度:

    标准程序

    #include <bits/stdc++.h>
    #define int long long 
    #define re register
    using namespace std;
    const int N=55;
    int h[N],f[N][N],min_dis[N][N],w1,w2,n,res,s1,ff; 
    signed main() {
      scanf("%lld %lld %lld",&n,&w1,&w2);
      for ( re int i=1;i<=n;i++ ) scanf("%lld",&h[i]),res+=h[i]*w2;
      sort(h+1,h+n+1);
      memset(f,63,sizeof(f));
      for ( re int i=0;i<=n;i++ ) 
          for ( re int j=i+1;j<=n;j++ ) 
              for ( re int k=i+1;k<=j-1;k++ ) 
                  min_dis[i][j]+=min(h[k]-h[i],h[j]-h[k]);
      for ( re int i=1;i<=n;i++ ) {
          f[i][1]=w1;
          for ( re int j=1;j<=i-1;j++ ) 
              f[i][1]+=w2*min(h[j],h[i]-h[j]);
      }
      for ( re int i=2;i<=n;i++ ) 
          for ( re int j=2;j<=i;j++ ) 
              for ( re int k=j-1;k<=i-1;k++ ) 
                  f[i][j]=min(f[i][j],w1+f[k][j-1]+min_dis[k][i]*w2);
      for ( re int i=1;i<=n;i++ ) 
          for ( re int j=1;j<=i;j++ ) {
              int tmp=0;
              for ( re int k=i+1;k<=n;k++ ) 
                  tmp+=(h[k]-h[i])*w2;
              res=min(res,tmp+f[i][j]);
      }
      printf("%lld\n",res);
      return 0;
    }

    深度优先搜索

  • 送分题

这道题目很简单,就好了

对于进行四进制转换,然后,扫一遍,统计转换后的每一种数字出现个数,输出出现次数最多的即可。

  • 思维难度:
  • 代码难度:

    标准程序

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=1000005,maxm=maxn*10+10; 
    char c[maxn];
    int num[maxm],n,m,ans,len;
    int main() {
      scanf("%s",c+1);
      len=strlen(c+1);
      scanf("%d",&n);
      for ( int i=1;i<=len;i++ ) {
          int sum=0;
          for ( int j=i;j<=min(len,i+n);j++ ) {
              if(c[i]=='A') sum=sum*1+1;
              if(c[i]=='G') sum=sum*2+1;
              if(c[i]=='C') sum=sum*3+1;
              if(c[i]=='T') sum=sum*4+1;
          }
          num[sum]++;
      }
      for ( int i=1;i<=10000000;i++ ) ans=max(ans,num[i]);
      printf("%d\n",ans);
      return 0;
    }
全部评论

相关推荐

01-17 10:58
四川大学 Java
点赞 评论 收藏
分享
点赞 评论 收藏
分享
评论
2
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务