国庆 day 2 下午

最大值(max)

Time Limit:1000ms   Memory Limit:128MB

 

题目描述

LYK有一本书,上面有很多有趣的OI问题。今天LYK看到了这么一道题目:

这里有一个长度为n的正整数数列ai(下标为1~n)。并且有一个参数k。

你需要找两个正整数x,y,使得x+k<=y,并且y+k-1<=n。并且要求a[x]+a[x+1]+…+a[x+k-1]+a[y]+a[y+1]+…+a[y+k-1]最大。

LYK并不会做,于是它把题扔给了你。

 

输入格式(max.in)

第一行两个数n,k。

    第二行n个数,表示ai。

 

输出格式(max.out)

两个数表示x,y。若有很多种满足要求的答案,输出x最小的值,若x最小仍然还有很多种满足要求的答案,输出y最小的值。 

 

输入样例

5 2

6 1 1 6 2

 

输出样例

1 4

 

对于30%的数据n<=100。

对于60%的数据n<=1000

对于100%的数据1<=n<=100000,1<=k<=n/2,1<=ai<=10^9。

 思路:维护连续的k个数的和 的后缀最大值

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define max(a,b) a>b?a:b
#define MAXV 500010
long long ma,mx,my,ans;
long long lx[MAXV],ly[MAXV],lt[MAXV],rt[MAXV];
long long a[MAXV],rtm[MAXV],sum[MAXV],summ[MAXV];
void ch(long long &x,long long y,long long z,int pos,int poss,int i){
    if(y<z){
        x=z;
        lx[i]=poss;
    }
    else{
        x=y;
        lx[i]=pos;
    }
}
void hc(long long &x,long long y,long long z,int pos,int poss,int i){
    if(y<z){
        x=z;
        ly[i]=poss;
    }
    else{
        x=y;
        ly[i]=pos;
    }
}
void cc(long long &x,long long y,int i){
    if(x<y){
        x=y;
        mx=lx[i-1];
        my=ly[i];
    }
}
int main(){
    freopen("max.in","r",stdin);
    freopen("max.out","w",stdout);
    int t,n,i,temp,k;
    scanf("%d%d",&n,&k);
    for(i=1;i<=n;i++){
        scanf("%d",&a[i]);
        sum[i]=sum[i-1]+a[i];
        ans+=a[i];
    }
    for(int i=1;i<=n;i++)
        summ[i]=ans-sum[i-1];
    for(int i=1;i<=k;i++)
        lt[i]=sum[i],lx[i]=1;
    for(int i=n;i>=n-k+1;i--)
        rt[i]=summ[i],ly[i]=i;
    for(int i=k+1;i<=n;i++)
        ch(lt[i],lt[i-1],sum[i]-sum[i-k],lx[i-1],i-k+1,i);
    for(int i=n-k;i>=1;i--)
        hc(rt[i],rt[i+1],summ[i]-summ[i+k],ly[i+1],i,i);
    for(i=k+1;i<=n-k+1;i++)
        cc(ma,lt[i-1]+rt[i],i);
    cout<<mx<<" "<<my;
    return 0;
}
View Code

吃东西(eat)

Time Limit:2000ms   Memory Limit:1024MB

 

题目描述

一个神秘的村庄里有4家美食店。这四家店分别有A,B,C,D种不同的美食。LYK想在每一家店都吃其中一种美食。每种美食需要吃的时间可能是不一样的。

现在给定第1家店A种不同的美食所需要吃的时间a1,a2,…,aA。

    给定第2家店B种不同的美食所需要吃的时间b1,b2,…,bB。

以及c和d。

LYK拥有n个时间,问它有几种吃的方案。

 

输入格式(eat.in)

第一行5个数分别表示n,A,B,C,D。

第二行A个数分别表示ai。

第三行B个数分别表示bi。

第四行C个数分别表示ci。

第五行D个数分别表示di。

 

输出格式(eat.out)

一个数表示答案。

 

输入样例

11 3 1 1 1

4 5 6

3

2

1

 

输出样例

2

 

对于30%的数据A,B,C,D<=50

对于另外30%的数据n<=1000。

对于100%的数据1<=n<=1000000001<=A,B,C,D<=5000,0<=ai,bi,ci,di<=100000000。

 30分的暴力:

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define MAXN 5010 
using namespace std;
int n,A,B,C,D;
long long ans,cnt1,cnt2;
int num1[MAXN*MAXN],num2[MAXN*MAXN];
int a[MAXN],b[MAXN],c[MAXN],d[MAXN];
struct nond{
    int x,y;
    bool operator < (nond b) const{
        return x>b.x;
    }
}v;
priority_queue<nond>que,q,qu;
int main(){
    freopen("eat.in","r",stdin);
    freopen("eat.out","w",stdout);
    scanf("%d%d%d%d%d",&n,&A,&B,&C,&D);
    for(int i=1;i<=A;i++)
        scanf("%d",&a[i]);
    sort(a+1,a+1+A);
    for(int i=1;i<=B;i++){
        scanf("%d",&b[i]);
        v.x=b[i]+a[1];v.y=1;
        que.push(v);
    }
    while(!que.empty()&&que.top().x<=n){
        nond now=que.top();
        num1[++cnt1]=now.x;
        que.pop(); 
        if(now.y+1<=A){
            now.x=now.x-a[now.y]+a[now.y+1];
            now.y+=1;
            que.push(now);
        }
    }
    for(int i=1;i<=C;i++)
        scanf("%d",&c[i]);
    sort(c+1,c+1+C);
    for(int i=1;i<=D;i++){
         scanf("%d",&d[i]);
         v.x=d[i]+c[1];v.y=1;
        q.push(v);
    }
    while(!q.empty()&&q.top().x<=n){
        nond now=q.top();
        num2[++cnt2]=now.x;
        q.pop();
        if(now.y+1<=C){
            now.x=now.x-c[now.y]+c[now.y+1];
            now.y+=1;
            q.push(now);
        }
    }
    for(int i=1;i<=cnt2;i++){
        v.x=num2[i]+num1[1];v.y=1;
        qu.push(v);
    }
    while(!qu.empty()&&qu.top().x<=n){
        nond now=qu.top();
        ans++;
        qu.pop();
        if(now.y+1<=cnt1){
            now.x=now.x-num1[now.y]+num1[now.y+1];
            now.y+=1;
            qu.push(now);
        }
    }
    cout<<ans;
}
View Code

正解思路:分组背包。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define MAXN 5005
#define M 25000005
#define N 100000005
using namespace std;
int A,B,C,D;
long long ans;
int n,cnt,cnt1,maxn;
int f[N],bns[M],cns[M];
int a[MAXN],b[MAXN],c[MAXN],d[MAXN];
int main(){
    freopen("eat.in","r",stdin);
    freopen("eat.out","w",stdout);
    scanf("%d%d%d%d%d",&n,&A,&B,&C,&D);
    for(int i=1;i<=A;i++)    scanf("%d",&a[i]);
    for(int i=1;i<=B;i++)    scanf("%d",&b[i]);
    maxn=0;
    for(int i=1;i<=A;i++)
        for(int j=1;j<=B;j++)
            if(a[i]+b[j]<=n){
                f[a[i]+b[j]]++;
                maxn=max(maxn,a[i]+b[j]);
            }
    for(int i=0;i<=maxn;i++)
        while(f[i]){
            f[i]--;
            bns[++cnt]=i;
        }
    for(int i=1;i<=C;i++)    scanf("%d",&c[i]);
    for(int i=1;i<=D;i++)    scanf("%d",&d[i]);
    maxn=0;
    for(int i=1;i<=C;i++)
        for(int j=1;j<=D;j++)
            if(c[i]+d[j]<=n){
                f[c[i]+d[j]]++;
                maxn=max(maxn,c[i]+d[j]);
            }
    for(int i=0;i<=maxn;i++)
        while(f[i]){
            f[i]--;
            cns[++cnt1]=i;
        }
    int now;
    for(now=cnt1;now>=1;now--)
        if(bns[1]+cns[now]<=n)
            break;
    for(int i=1;i<=cnt;i++){
        ans+=now;
        while(now&&bns[i+1]+cns[now]>n)    now--;
    }
    cout<<ans;
}
View Code

分糖果(candy)

Time Limit:1000ms   Memory Limit:128MB

 

题目描述

总共有n颗糖果,有3个小朋友分别叫做L,Y,K。每个小朋友想拿到至少k颗糖果,但这三个小朋友有一个共同的特点:对3反感。也就是说,如果某个小朋友拿到3颗,13颗,31颗,333颗这样数量的糖果,他就会不开心。(也即它拿到的糖果数量不包含有一位是3)

LYK掌管着这n颗糖果,它想问你有多少种合理的分配方案使得将这n颗糖果全部分给小朋友且没有小朋友不开心。

例如当n=3,k=1时只有1种分配方案,当n=4,k=1时有3种分配方案分别是112,121,211。当n=7,k=2时则不存在任何一种合法的方案。

当然这个答案可能会很大,你只需输出答案对12345647取模后的结果就可以了。

 

输入格式(candy.in)

第一行两个数表示n,k。

 

输出格式(candy.out)

一个数表示方案总数。

 

输入样例

99999 1

 

输出样例

9521331

 

对于30%的数据n<=100

对于50%的数据n<=1000。

对于另外30%的数据k=1。

对于100%的数据3<=n<=10^10000,1<=k<=n/3,且n,k不包含前导0。

 

数位DP

dp[i][j][k][l][t] 表示n的前i位,分完后的余数为j,第1/2/3个小朋友的第i位能否随便填 的方案数

再枚举3个小朋友分别分多少转移

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define MAXN 10003
#define mod 12345647
using namespace std;
char s[MAXN];
int a[MAXN],b[MAXN];
int dp[MAXN][3][2][2][2];
void ADD(int &i,int j){
    i+=j;
    if(i>=mod)    i-=mod;
}
int main(){
    freopen("candy.in","r",stdin);
    freopen("candy.out","w",stdout);
    scanf("%s",s+1);
    int len1=strlen(s+1);
    for(int i=1;i<=len1;++i)
        a[i]=s[i]-'0';
    scanf("%s",s+1);
    int len2=strlen(s+1);
    for(int i=1;i<=len2;++i)
        b[i+len1-len2]=s[i]-'0';
    dp[0][0][0][0][0]=1;
    for(int i=0;i<len1;i++)
        for(int j=0;j<3;j++)
            for(int k=0;k<2;k++)
                  for(int l=0;l<2;l++)
                    for(int t=0;t<2;t++)
                        if(dp[i][j][k][l][t])
                              for(int s1=0;s1<=9;s1++)
                                if(s1!=3)
                                      for(int s2=0;s2<=9;s2++)
                                        if(s2!=3)
                                              for(int s3=0;s3<=9;s3++)
                                                if(s3!=3){
                                                    int I=i+1;
                                                    int J=j*10+a[i+1]-s1-s2-s3;
                                                    if(J<0||J>2) continue;
                                                    if(!k&&s1<b[i+1]) continue;
                                                    int K=(k||s1>b[i+1]);
                                                    if(!l&&s2<b[i+1]) continue;
                                                    int L=(l||s2>b[i+1]);
                                                    if(!t&&s3<b[i+1]) continue;
                                                    int T=(t||s3>b[i+1]);
                                                    ADD(dp[I][J][K][L][T],dp[i][j][k][l][t]);                    
                                                }
    int ans=0;
    for(int k=0;k<2;k++)
        for(int l=0;l<2;l++)
            for(int t=0;t<2;t++)
                ADD(ans,dp[len1][0][k][l][t]);
    printf("%d",ans);                        
}
View Code
细雨斜风作晓寒。淡烟疏柳媚晴滩。入淮清洛渐漫漫。 雪沫乳花浮午盏,蓼茸蒿笋试春盘。人间有味是清欢。
原文地址:https://www.cnblogs.com/cangT-Tlan/p/7641172.html