2021寒假集训-Updating

Day1


 一、
codeforces
题目描述 
牛牛正在打一场CF
比赛时间为T分钟,有N道题,可以在比赛时间内的任意时间提交代码
第i道题的分数为maxPoints[i],题目的分数随着比赛的进行,每分钟减少pointsPerMinute[i]
这是一场比较dark的Cf,分数可能减成负数
已知第i道题需要花费 requiredTime[i] 的时间解决
请问最多可以得到多少分

输入描述:
第一行输入两个整数N,T (1 ≤ N ≤ 50, 1 ≤ T ≤ 100000)
第二行输入n个整数maxPoints[i]
第三行输入n个整数pointsPerMinute[i]
第四行输入n个整数requiredTime[i]
1 ≤ maxPoints[i],pointsPerMinute[i],requiredTime[i] ≤ 100000
输出描述:
输出一个整数
示例1
输入
1 74
502
2
47
输出
408
示例2
输入
2 40000
100000 100000
1 100000
50000 30000
输出
0
示例3
输入
3 75
250 500 1000
2 4 8
25 25 25
输出
1200
示例4
输入
3 30
100 100 100000
1 1 100
15 15 30
输出
97000
备注:
子任务1: n <= 10
子任务2: n <= 20
子任务3: 无限制

题解:
对于这个题,做题的先后顺序会影响得分,所以得知道先做哪个后做哪个。如何选择做题顺序?
推导如下:
先假设有两个题,A题和B题,设lessA为A题每分钟减少的分数,lessB为B题每分钟减少的分数,timeA为做A题所需要的时间,timeB为做B题所需要的时间。A与B题的max总分数为sum。
score(AB)=sum-timeA*lessA-(timeA+timeB)*lessB;
score(BA)=sum-timeB*lessB-(timeA+timeB)*lessA。
二式相减得:score(AB)-score(BA)=timeB*lessA-timeA*lessB。
又因为最终要求得是最高得分,故我们需要把得分相对高的并且减小相对慢的放在前面选。上式即可作为判断依据。
 

struct node
{
    long long int v;
    long long int less;
    long long int w;
    long long int time;
}te[maxn];

bool cmp(node x,node y)
{
    return x.time*y.less<y.time*x.less;
} 

处理完此处后,就是一个正常的01背包问题了。

#include <bits/stdc++.h>

using namespace std;

const int maxn=100005; 

int main()
{
    //n个题 t分钟 
    int n,t;
    scanf("%d %d",&n,&t);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&te[i].v);
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&te[i].less);
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&te[i].time);
    }
    sort(te+1,te+1+n,cmp);
    for(int i=1;i<=n;i++)
    {
        for(int j=t;j>=te[i].time;j--)
        {
            dp[j]=max(dp[j],dp[j-te[i].time]+te[i].v-te[i].less*j);
        }
    }
    long long int ans=0;
    for(int i=t;i>=0;i--)
    {
        ans=max(ans,dp[i]);
    //    printf("dp=%d\n",dp[i]);
    }
    //printf("%d\n",dp[t]);
    printf("%lld\n",ans);
    
    return 0;
}


ps:
1.开long long
2.最后需要遍历一下取max

 二、
埃氏筛 O(nloglogn)

基本思想 :从2开始,将每个质数的倍数都标记成合数,以达到筛选素数的目的。

缺陷 :对于一个合数,有可能被筛多次。例如 30 = 2 * 15 = 3 * 10 = 5*6……如何确保每个合数只被筛选一次?要用它的最小质因子来筛选,即欧拉筛。

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
 
int prime[maxn];
int cnt;
bool vis[maxn];
 
void get_primes(int n) 
{
    memset(vis,false,sizeof(vis));
    for(int i=2;i<=n;i++) 
    {
        if(vis[i]==false)
        { 
            prime[cnt++]=i;
            for(int j=i;j<=n;j+=i)
            {
                vis[j]=true;
            }    
        }
    } 
}
 
int main() 
{
    int x;
    cin>>x;
    get_primes(x);
    cout<<cnt<<endl;
    for(int i=0;i<cnt;i++)
    {
        printf("prime=%d\n",prime[i]);
    }
    return 0;
}


线性筛(欧拉筛) O(n)   n = 1e7的时候基本就比埃式筛法快一倍了
基本思想:在埃氏筛法的基础上,让每个合数只被它的最小质因子筛选一次,以达到不重复的目的。x 仅会被其最小质因子筛去。

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
 
int prime[maxn];
int cnt;
bool vis[maxn];
 
void get_prime(int x) 
{
    memset(vis,false,sizeof(false));
    for(int i=2;i<=x;i++) 
    {
        if(vis[i]==false)
        {
            prime[cnt++]=i;
        } 
        for(int j=0;prime[j]<=x/i;j++) 
        {
            st[prime[j]*i]=true;
            if(i%prime[j]==0)
            break;
        }
    } 
}
int main() 
{
    int x;
    cin>>x;
    get_prime(x);
    cout<<cnt<<endl;
    for(int i=0;i<cnt;i++)
    {
        printf("prime=%d\n",prime[i]);
    } 
    return 0;
}


三、
线段树(没写完)
1.单点修改,区间查询  QAQ 
2.区间修改,单点查询  QAQ 
3.区间加法   QAQ 
4.区间乘法   QAQ 
5.区间根号   QAQ

 

Day2

 


牛牛想起飞 
王老板题的压缩成一维的。

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;

int a[maxn],b[maxn];

int dp[105];
int main()
{
    int n,y;
    scanf("%d %d",&n,&y);
    int sum=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        sum=(sum+a[i])%y;
    }
    dp[sum]=1;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&b[i]);
        for(int j=0;j<y;j++) 
        {
            if(dp[j]==1)
            {
                if(dp[(j+b[i])%y]==0)
                dp[(j+b[i])%y]=2;
                if(dp[(j-b[i]+y)%y]==0)
                dp[(j-b[i]+y)%y]=2;
            }
        }
        for(int j=0;j<y;j++)
        {
            if(dp[j]==2)
            dp[j]=1;
        }
    }
    int maxx=0;
    for(int i=0;i<y;i++)
    {
        if(dp[i]==1)
        {
            maxx=max(maxx,i);
        }
    }
    printf("%d\n",maxx);   
    return 0;
}

Day3

Mowing the Lawn G
思路1: O(N^2)算法

这题转化后就是求在N个数每K+1个数删去一个数,并求最大值。

比较暴力的就是DP从1到N枚举,第二维从i到N枚举,然后,前缀和+DP乱搞。


考虑优化:

在第i点时,在i-k到i中肯定有一个点j不能选择,即:j为断点。(题意得来的)

所以
dp[i]=max(dp[i],dp[j-1]+a[j+1]+a[j+2]……a[i])(i-k<=j<=i)

用前缀和化简一下

dp[i]=max(dp[i],dp[j-1]+sum[i]-sum[j]) (i-k<=j<=i)

变形一下

dp[i]=max(dp[i],dp[j-1]-sum[j])+sum[i] (i-k<=j<=i)

又如果一头奶牛比当前枚举到的奶牛小,还比他弱,那么这头奶牛就没有用了,我们可以用这个方法来维护一个单调上升的序列,也就是单调队列。也就是说,用单调队列来维护dp[j-1]-sum[j]最大。


单调队列涉及去头,删尾两个操作。

什么时候去头呢?

当头位置的已经没法再给当前位置做贡献的时候(即已经超出K+1的限制的时候),去头,因为他已经没有用了。

什么时候删尾呢?

当尾部的位置不如当前位置的时候,把尾部位置的牛替换为当前牛,以获得最大收益。

复杂度是可以降到O(N)的,差一点的估计就是O(KN)
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值