NOIP模拟赛1 飞盘队(貌似又是USACO上面的题) MR_HE改编 子集生成+中途相遇+dp

来源:互联网 发布:会计软件的合法性 编辑:程序博客网 时间:2024/05/02 03:12

【问题描述】
农夫顿因开始玩飞盘之后,约翰也打算让奶牛们享受飞盘的乐趣。他打算组建一只自己的奶牛飞盘队。
他的N只奶牛里,每只都有一个飞盘水准指数R_i,约翰要选出其中一些奶牛来参加他的飞盘队。由于约翰的幸运数字是F,所以他希望所选的奶牛的飞盘水准指数之和是辛运数字F的倍数。
请你帮助约翰计算一共有多少种组队方式。

【输入格式】
从文件fristeam.in中读入数据。
输入的第1行是N和F,它们的意义如题目描述,接下来的N行,每行包含一个整数,其第i+1行的整数为R_i,表示第i只奶牛的飞盘水准指数。

【输出格式】
输出到文件fristeam.out中。
输出包含一个整数,表示约翰可能的组队方案数,如果这个数是一个超过了8位的整数,那么你只须输出最低的8位即可,不要输出高位的0。

【输入样例1】
4 5
1
2
8
2

【输出样例1】
3

【样例说明】
第1种合法的方案:第2、3只牛组队,他们的飞盘指数和为2+8=10。
第2种合法的方案:第3、4只牛组队,他们的飞盘指数和为8+2=10。
第3种合法的方案:第1、2、4只牛组队,他们的飞盘指数和为1+2+2=5。

【输入样例2】
5 7
2
6
1
7
8

【输出样例2】
5

这里写图片描述

——————————————————————————————————————————————————————————

显然前面10组后后面10组画风都不一样,前面十组由于F太大,直接子集生成+中途相遇,复杂度O(2^(N/2)*log2(2^(N/2))),后面直接一发dp,f(i,j)表示在前i只奶牛中选出一些来建队使得和对F取模结果为j的方案数,f(i,j)=(f(i-1,j)+f(i-1,(j-a[i]%F+F)%F))%100000000,ans=f(N,0)-1。(除去所有奶牛都不选的情况)

AC代码:

#include<iostream>#include<cstdio>#include<cstring>#include<cstdlib>#include<algorithm>#include<cmath>#include<queue>#include<set>#include<map>#include<vector>#include<cctype>#include<ctime>using namespace std;const int maxn=2005;const int mo=100000000;const int mon=1000007;int N,F,R[maxn];int f[2][10005];int A[1050000],cnt;int ans;void data_in(){    scanf("%d%d",&N,&F);    for(int i=1;i<=N;i++)        scanf("%d",&R[i]);}void run1(int i,int sum){    if(i>N/2)    {        A[++cnt]=sum;        return;    }    run1(i+1,sum);    run1(i+1,(sum+R[i])%F);}void run2(int i,int sum){    if(i>N)    {        int x,s,t;        x=(F-sum)%F;        s=lower_bound(A+1,A+cnt+1,x)-A;        t=upper_bound(A+1,A+cnt+1,x)-A;        ans=(ans+t-s)%mo;        return;    }    run2(i+1,sum);    run2(i+1,(sum+R[i])%F);}void work1(){    run1(1,0);    sort(A+1,A+cnt+1);    run2(N/2+1,0);    printf("%d\n",(ans-1+mo)%mo);}void dp2(){    f[0][0]=1;    for(int i=1;i<=N;i++)    for(int j=0;j<F;j++)        f[i&1][j]=(f[(i-1)&1][j]+f[(i-1)&1][((j-R[i])%F+F)%F])%mo;}void work2(){    dp2();    printf("%d\n",f[N&1][0]-1);}int main(){    freopen("fristeam.in","r",stdin);    freopen("fristeam.out","w",stdout);    data_in();    if(N<50) work1();    else work2();    return 0;}
阅读全文
0 0
原创粉丝点击