向dalao致敬
JZOJ NO.1 公牛和母牛
分析
dp,
f
[
i
]
f[i]
f[i]表示
i
i
i头牛的方案
当母牛时,加入一头牛。
当公牛时,为加入
k
k
k头母牛。
f
[
i
]
=
f
[
i
−
1
]
+
1
(
i
<
=
k
)
f[i]=f[i-1]+1 (i<=k)
f[i]=f[i−1]+1(i<=k)
f
[
i
]
=
f
[
i
−
1
]
+
f
[
i
−
k
]
(
i
>
k
)
f[i]=f[i-1]+f[i-k] (i>k)
f[i]=f[i−1]+f[i−k](i>k)
代码
#include <cstdio>
using namespace std;
int n,m,f[100001];
int main(){
scanf("%d%d",&n,&m); f[0]=1; m++;
for (int i=1;i<=m;i++) f[i]=i;
for (int i=m+1;i<=n;i++) f[i]=(f[i-1]+f[i-m])%5000011;
printf("%d",f[n]);
return 0;
}
JZOJ NO.2 最短路
分析
求n+C(n+m+1,m)
代码
#include <cstdio>
#define ull unsigned long long
using namespace std;
ull ans,ahs=1,n,m;
const ull mod=1e9+7;
ull qp(ull x,ull y){
ull s=1;
while (y){
if (y&1) s=(s*x)%mod;
y>>=1; x=(x*x)%mod;
} return s;
}
int main(){
scanf("%llu%llu",&n,&m);
if (n<m) n^=m,m^=n,n^=m; ans=(n+m+1)%mod;
for (ull i=2;i<=m;i++) ans=ans*(n+i)%mod,ahs=ahs*i%mod;
ans=ans*qp(ahs,mod-2)%mod;//逆元
printf("%llu",(ans%mod+n)%mod);
return 0;
}
JZOJ NO.3 Magical GCD
分析
从左到右模拟O(T*N^2/gcd)必然会超时,所以就定义左右指针。相同的保左,右覆盖就好了。
代码
#include <cstdio>
#include <cctype>
#define ull unsigned long long
using namespace std;
ull a[100001],g[100001],max; int t,n,r[100001],l[100001];
ull gcd(ull a,ull b){if (b) return gcd(b,a%b); else return a;}
ull in(){
char c=getchar(); ull ans=0;
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans;
}
int main(){
t=in();
while (t--){
max=0; n=in();
for (int i=1;i<=n;i++){
a[i]=in();
g[i]=a[i]; l[i]=i-1; r[i]=i+1;
//g[i]表示第i个数与n个数的最大公因数,l、r左右指针
}
for (int x=1;x<=n;x++)
for (int i=1;i<=x;i=r[i]){
g[i]=gcd(g[i],a[x]);
ull c=g[i]*(x-i+1);
max=(c>max)?c:max;
if (g[i]==g[i-1]) //相同还需要重复运算吗
r[l[i]]=r[i],l[r[i]]=l[i];
}
printf("%llu\n",max);
}
return 0;
}
JZOJ NO.4 Multiset
题目
Alice 正在玩一个 multiset。最初,集合中只有一个元素 0。每一轮,集合中的每一个元素 x 都有 3 种可能的操作:
1、x 加上 1.即 x = x +1。
2、x 分裂成两个非负整数 y, z。即 x = y + z, 且 y >=0, z >= 0。
3、什么都不做。
注意,在一轮中每个元素只能选择一种操作。
Alice 已经玩了很久了, 但她并不知道自己已经玩了多少轮。 现在给出最终的集合,请你输出 Alice 最少玩的轮数。
分析
首先倒推一下,无非合并和-1。
所以就用桶模拟一下。
代码
#include <cstdio>
#include <cctype>
using namespace std;
unsigned short b[1000001]; int s,n,x,max;
int in(){
char c=getchar(); int ans=0;
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans;
}
int main(){
n=in(); for (int i=1;i<=n;i++) x=in(),b[x]++,max=(max>x)?max:x; s=b[0];//max一开始肯定最大的数
for (int i=1;i<=max;i++) s=((s+1)>>1)+b[i];//0先合并
while (s>1){s=(s+1)>>1;max++;}//合并非0
printf("%d",max); return 0;
}