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; }

 京公网安备 11010502036488号
京公网安备 11010502036488号