国庆清北刷题冲刺班 Day2 下午

来源:互联网 发布:python drdos 编辑:程序博客网 时间:2024/05/04 11:19

最大值(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]最大。
K<=n/2
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。.

//考场爆零代码 ==>   本场比赛直接GG   120 分  草草收场#include<iostream>#include<cstring>#include<cstdio>#include<cstdlib>using namespace std;inline void read(int &x){    x=0; int f=1; char c=getchar();    while(c>'9'||c<'0'){ if(c=='-')f=-1; c=getchar(); }    while(c>='0'&&c<='9'){ x=x*10+c-'0'; c=getchar(); } x*=f;}#define MAXN 100005int n,k,a[MAXN];long long sum[MAXN];int main(int argc,char *argv[]){    freopen("max.in","r",stdin);    freopen("max.out","w",stdout);    read(n),read(k);    int Ansi,Ansj;    long long Max=0;    for(int i=1;i<=n;++i) read(a[i]),sum[i]=sum[i-1]+a[i];    if(k==n/2){ printf("1 %d",n-k+1); exit(0); }    for(register int i=1;i<=n;++i)        for(register int j=i+k;j+k-1<=n;++j)            if(sum[i+k-1]-sum[i-1]+sum[j+k-1]-sum[j-1]>Max){                Max=sum[i+k-1]-sum[i-1]+sum[j+k-1]-sum[j-1];                Ansi=i,Ansj=j;            }    printf("%d %d\n",Ansi,Ansj);    fclose(stdin);fclose(stdout);    return 0;}

分析:一个O(n^3)的做法,直接枚举两个区间,再枚举求区间和.因为用到了区间和,所以可以用前缀和优化到O(n^2).然后可以发现这个区间长度是固定的,我们可以在挪动右端点的时候右边每加一个数,左边弹一个数,用O(n)的时间处理出每一段长度为k的区间的和,在处理的过程中可以顺便记录j-k之前的区间最大值,一边求和一边统计答案就可以了。

#include<iostream>#include<cstring>#include<cstdio>using namespace std;typedef long long LL;int n,k,lastt=1,x,y;LL a[100010],r[100010],Sum,Ans,Max;int main(int argc,char *argv[]){    freopen("max.in","r",stdin);    freopen("max.out","w",stdout);    scanf("%d%d",&n,&k);    for(int i=1; i<=n; ++i) scanf("%lld",&a[i]);    for(int i=1; i<=n; ++i){        Sum += a[i];        if(i - k > 0) Sum -= a[i - k];        r[i] = Sum;        if(i - k > 0)            if(Max < r[i - k]) Max = max(Max,r[i - k]),lastt=i - k;        if (Max + r[i] > Ans) {            Ans = max(Ans,Max + r[i]);            x = max(1,lastt - k + 1);            y = max(1,i - k + 1);        }    }    printf("%d %d\n",x,y);    fclose(stdin);fclose(stdout);    return 0;}

吃东西(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<=100000000,1<=A,B,C,D<=5000,0<=ai,bi,ci,di<=100000000。

算法剖析:Meet in The Middle

#include<iostream>#include<cstring>#include<cstdio>#include<algorithm>using namespace std;typedef long long LL;#define MAXN 5050int a[MAXN],c[MAXN],d[MAXN],b[MAXN];int f[100000005],c1[25000010],c2[25000010],cnt1,cnt2;LL Ans=0;inline void read(int &x){    x=0; int f=1; char c=getchar();    while(c>'9'||c<'0'){ if(c=='-')f=-1; c=getchar(); }    while(c>='0'&&c<='9'){ x=x*10+c-'0'; c=getchar(); } x*=f;}int main(int argc,char *argv[]){    freopen("eat.in","r",stdin);    freopen("eat.out","w",stdout);    int n,A,B,C,D,maxn = 0,cur;    read(n),read(A),read(B),read(C),read(D);    for(int i=1; i<=A; ++i) read(a[i]);    for(int i=1; i<=B; ++i) read(b[i]);    for(int i=1; i<=C; ++i) read(c[i]);    for(int i=1; i<=D; ++i) read(d[i]);    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],c1[++cnt1]=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],c2[++cnt2]=i;    for(cur = cnt2; cur>=1; --cur)        if(c1[1] + c2[cur] <= n) break;    LL Ans = 0;    for(int i=1; i<=cnt1; ++i){        Ans += cur;        while(cur && c2[cur] + c1[i+1] > n) --cur;    }    printf("%I64d\n",Ans);    fclose(stdin);fclose(stdout);    return 0;}

分糖果(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。

前面50分暴力,两层数据直接不是一个等级。。。

分析:这道题也不是一道特别简单的数位dp,因为要3个数的和等于n,所以我们可以在每一数位的时候枚举3个数上的这一位的值,它们的和与n的第i位相差是≤2的,因为进位最多进两位。同时还有≥k这个限制,所以我们可以设状态f[i][j][kk][l][p]表示前i位,第i+1位要向第i位进j位,kk,l,p表示枚举的3个数是否>k.每次递推的时候就能知道下一个状态,就能够就行转移了。
要求方案数,数字位数又这么多,能想到的算法只有数位dp了,从数字看向数位,是一种很好的思想的转变,如果数位dp有数字大小的限制,那么通用的办法就是加一维表示是否超出限制即可。(腿老师【张浩威】出题就是有水平啊)

//数位DP#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>using namespace std;const int mod = 12345647;char n[10010], k[10010];int a[10010],b[10010],f[10010][3][2][2][2];int main() {    freopen("candy.in","r",stdin);    freopen("candy.out","w",stdout);    scanf("%s", n + 1);    int len1 = strlen(n + 1);    scanf("%s", k + 1);    int len2 = strlen(k + 1);    int Ans = 0;    for (int i = 1; i <= len1; i++) a[i] = n[i] - '0';    for (int i = 1; i <= len2; i++) b[i + len1 - len2] = k[i] - '0';    f[0][0][0][0][0] = 1;    for (int i = 0; i < len1; i++)    for (int j = 0; j <= 2; j++)    for (int k = 0; k <= 1; k++)    for (int l = 0; l <= 1; l++)    for (int p = 0; p <= 1; p++)    if (f[i][j][k][l][p])    for (int q = 0; q <= 9; q++)    if (q != 3)    for (int q2 = 0; q2 <= 9; q2++)        if (q2 != 3)            for (int q3 = 0; q3 <= 9; q3++)                if (q3 != 3) {                    int ni = i + 1, nj = j * 10 + a[i + 1] - q - q2 - q3;                    int nk, nl, np;                    if (nj < 0 || nj > 2) continue;                    if (k == 0 && q < b[ni]) continue;                    nk = (k || q > b[ni]);                    if (l == 0 && q2 < b[ni]) continue;                    nl = (l || q2 > b[ni]);                    if (p == 0 && q3 < b[ni]) continue;                    np = (p || q3 > b[ni]);                    f[ni][nj][nk][nl][np] += f[i][j][k][l][p];                    if (f[ni][nj][nk][nl][np] >= mod)                        f[ni][nj][nk][nl][np] -= mod;                }    for (int i = 0; i <= 1; i++)        for (int j = 0; j <= 1; j++)            for (int k = 0; k <= 1; k++) {                Ans += f[len1][0][i][j][k];                if (Ans >= mod)                    Ans -= mod;            }    printf("%d\n", Ans);    fclose(stdin);fclose(stdout);    return 0;}