E:楼军的数学题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zqVuAtoI-1650807917519)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220417085440466.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nANKXyDr-1650807917520)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220417085451186.png)]
//最基础想法
for(i = 1;i <= n; i++){//前i种
for(j = 0;j <= m; j++){
//在前i种字符里面选了j个数字
for(k = 0;k <= min(c[i],j); k++){//min的作用是保证不越界访问
dp[i][j] = (dp[i][j] + dp[i-1][j-k])%mod;
//对一个字符,它最多能用c[i]次,因为从j-c[i]到j都可以取这个字符,dp[i][j]可以由前面的转移过来,再说简单一点就是j前面连续选择k个c[i]对应的那个数字即i;
//可以从dp[i-1][j-k]开始选择k个数i,组成dp[i][j];
}
}
}
当然这种做法最大时间复杂度达到了1e9,肯定会超时;
因为最内层的二重循环其实就是对于字符取或者不取的0/1背包问题,则我们可以优化;
//上一种写法种k取0是因为要将上一轮i-1的到的结果赋值到目前i这一轮来,所以要从0开始,而下面这种写法
//不用将上一轮赋值到下一轮,所以k不用从0开始;
dp[0] = 1;
for(i =1;i <= n; i++){
for(j = m;j >= 0; j--){
for(k = 1;k <= min(c[i],j); k++){
dp[j] = (dp[j] + dp[j-k])%mod;
}
}
}
而对于最内层内层其实就是将dp[j-c[i]]到dp[j-1]这一段累加起来,因此我们可以采用前缀和来存储;
for(i = 1;i <= n; i++){
for(j = m;j >= 1;j--){
dp[j] = (dp[j]+sum[j-1]-sum[j-min(a[i],j)-1]+mod)%mod;
}
for(j = 1;j <= m; j++){//更新前缀和数组,为下一次计算做准备
sum[j] = (sum[j-1] + dp[j])%mod;
}
}
完整代码;
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e3+5,mod = 1e9+7;
int c[N];
ll sum[N],dp[N];
int main()
{
int i,j;
int n,m;scanf("%d%d",&n,&m);
for(i = 1;i <= n; i++)
scanf("%d",&c[i]);
for(i = 0;i <= m; i++)
sum[i] = 1;
dp[0] = 1;
for(i = 1;i <= n; i++){
for(j = m;j >= 1; j--){
int k = j - min(j,c[i]) - 1;
if(k < 0){
dp[j] = (dp[j] + sum[j-1])%mod;
}else{
dp[j] = (dp[j] + sum[j-1] - sum[k] + mod)%mod;
}
}
//更新前缀和数组
for(j = 1;j <= m; j++){
sum[j] = (sum[j-1] + dp[j])%mod;
}
}
cout<<dp[m]<<endl;
return 0;
}
K:上升的海平面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-effrSlTO-1650807917521)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220417093732565.png)]
记录山谷和山峰,山谷被淹没时,陆地被分割成两块陆地,山峰被淹没时,有一块陆地被淹没;
注意连续区域高度相等的情况;
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5+5;
int a[N];
int hup[N];//山峰
int hdown[N];//山谷
int main()
{
int i,j;
int t,n,n2;scanf("%d%d",&t,&n);
for(i = 1,n2=1;i <= n; i++,n2++){
scanf("%d",&a[n2]);
if(a[n2] == a[n2-1]){
n2--;
}
}
a[n2] = -1;//1至n2-1为实际区域
//找出山峰和山谷
int cntup = 0,cntdown = 0;
for(i = 1;i < n2; i++){
if(a[i]>a[i-1]&&a[i]>a[i+1]){
hup[cntup++] = a[i];
}
if(a[i]<a[i-1]&&a[i]<a[i+1]){
hdown[cntdown++] = a[i];
}
}
sort(hup,hup+cntup);
sort(hdown,hdown+cntdown);
while(t--){
int k;scanf("%d",&k);
int q = upper_bound(hdown,hdown+cntdown,k)-hdown;
int w = upper_bound(hup,hup+cntup,k)-hup;
cout<<q-w+1<<endl;
}
return 0;
}
F:YY的最短路
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T1tVuxFi-1650807917522)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220417162117491.png)]
思路:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CspKMMEx-1650807917522)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220417162232973.png)]
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e6+5;
const int mod = 1e9+7;
int a[N];
int main()
{
int i,j;
int n;scanf("%d",&n);
int flag = 0;
for(i = 1;i <= n; i++){
scanf("%d",&a[i]);
if(a[i] == 0) flag++;
}
if(flag!=1){
cout<<0<<endl;
return 0;
}
ll ans = 1;
sort(a+1,a+n+1);
for(i = 2;i <= n; i++){
int idx = lower_bound(a+1,a+i,a[i])-a;
ans = (ans*(idx-1))%mod;
}
cout<<ans<<endl;
return 0;
}
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+5;
int n;
int a[N];
void ex(int c[],int &l,int &r,int &total)
{
total-=(r-l+1);
total=max(0,total);
for(int i=l;i<=r;i++){
if(c[i]>1){
c[i]--;
}else{
l++;//左边的都用不了了
}
}
}
bool check(int x,int k)
{
int b[N] = {0};//用来记录 前面的点贡献的的攻击次数
for(int i=1;i<=n;i++) b[i] = 0;
int cnt = 0;//总共攻击多少次
int red = 0,t = 0;//攻击值衰减应该减的数量,攻击值
for(int i =n;i > 0; i--){
red -= b[i];//到这个点为止,前面的点贡献的攻击次数已经没了
t-=red;//还有的攻击值
if(a[i] > t){//这个点的生命值大于前面所有点贡献的攻击伤害的话,也就是要多攻击一次
int ex = ceil((double)(a[i]-t)/x);//需要多攻击几次?;(a[i]-t-1)/m+1;
red += ex;
cnt += ex;
if(cnt > k) return false;
t += ex*x;
if(i-x>1) b[i-x-1] = ex;//到i-x-1这个点那i这一次贡献的攻击衰减就没了
}
}
return true;
}
int main()
{
int i,j;
int k;scanf("%d%d",&n,&k);
int maxt = 0;//最大攻击力
for(i=1;i<=n;i++){
scanf("%d",&a[i]);
maxt = max(maxt,a[i]+n-i);
}
int l = 1,r = maxt;
while(l<r){
int mid = (l+r)>>1;
if(check(mid,k)) r = mid;
else l = mid+1;
}
cout<<l;
}