NOIP2016 Day2

来源:互联网 发布:java飞机大战子弹移动 编辑:程序博客网 时间:2024/06/01 23:18

T1 组合数问题

由于k是固定的 可以预处理出模k意义下的杨辉三角
然后累计二维前缀和 就可以O(1)回答询问了
O(T+nm)

#include<bits/stdc++.h>using namespace std;#define N 2010int K,C[N][N],sum[N][N];void init(){    int i,j;    for (i=0;i<=2000;i++){        C[i][0]=1;        for (j=1;j<=i;j++)            C[i][j]=(C[i-1][j-1]+C[i-1][j])%K;    }    for (i=1;i<=2000;i++)        for (j=1;j<=2000;j++)            sum[i][j]=sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1]+(C[i][j]==0 && j<=i);}int main(){    int T;    scanf("%d%d",&T,&K);    init();    while (T--){        int n,m;        scanf("%d%d",&n,&m);        printf("%d\n",sum[n][min(n,m)]);    }    return 0;}

T2 蚯蚓

直接用堆维护可得85分左右
q=0的情况可以直接计数
所以正解其实只占了五分

首先要找到一个很显然的性质
两只蚯蚓切开之前和切开之后的对应两部分的相对位置始终保持不变
因此按顺序切开后 如果把两部分对应存储 那么也一定是按顺序从大到小
那么就不需要在过程中排序了
只需要把原始的蚯蚓 和切开的两部分用三个数组(队列)存下来 每次找最大的一个队首 然后把切下来的两部分放到对应的队列尾部即可
O(n+m)(除去一开始的排序)

#include<bits/stdc++.h>using namespace std;#define N 100010#define M 7000010int n,m,q,t,a[N];double p;int Q1[N+M],Q2[N+M],Q3[N+M],l1,l2,l3,r1,r2,r3;inline int Top(){    int t1=-2e9,t2=-2e9,t3=-2e9;    if (l1<=r1) t1=Q1[l1];    if (l2<=r2) t2=Q2[l2];    if (l3<=r3) t3=Q3[l3];    if (t1>=t2 && t1>=t3){        l1++;        return t1;    }    if (t2>=t1 && t2>=t3){        l2++;        return t2;    }    if (t3>=t1 && t3>=t2){        l3++;        return t3;    }}int main(){    int i,u,v;    scanf("%d%d%d%d%d%d",&n,&m,&q,&u,&v,&t);    p=1.0*u/v;    for (i=1;i<=n;i++) scanf("%d",&a[i]);    sort(a+1,a+1+n);    l1=l2=l3=1;    for (i=n;i>=1;i--) Q1[++r1]=a[i];    int tim=1,inc=0;    for (i=1;i<=m;i++,tim++){        int tmp=Top()+inc;        int t1=(int)floor(tmp*p);        int t2=tmp-t1;        inc+=q;        Q2[++r2]=t1-inc;        Q3[++r3]=t2-inc;        if (tim==t) printf("%d ",tmp),tim=0;    }    printf("\n");    for (i=1,tim=1;i<=n+m;i++,tim++){        int tmp=Top();        if (tim==t) printf("%d ",tmp+inc),tim=0;    }    return 0;}

T3 愤怒的小鸟

n18 看起来就是状压dp或搜索

主要是要处理出可行的抛物线
那么先枚举两个点确定一条线 把能经过的点都找出来
只存储包含所有能经过点的状态集合
因此最多只有n条抛物线
那么就可以状压了
BFS转移可以减少枚举到无用的状态
O(n3+2nn)
要注意精度问题

#include<bits/stdc++.h>using namespace std;#define N 20#define EPS 1e-6struct poi{    double x,y;}a[N];int n,m;struct pwx{    bool ex;    double a,b;};int dp[1<<N],Q[1<<N],h;bool b[1<<N];queue<int>G;inline pwx jfc(poi A,poi B){    pwx res;    res.ex=1;    double x1=A.x*A.x,x2=B.x*B.x;    if (fabs(A.x*x2-x1*B.x)<EPS) res.ex=0;    res.b=(A.y*x2-x1*B.y)/(A.x*x2-x1*B.x);    res.a=(A.y-res.b*A.x)/x1;    if (res.a>-EPS) res.ex=0;    return res;}inline bool contain(pwx A,poi B){    return fabs(A.a*B.x*B.x+A.b*B.x-B.y)<EPS;}void init(){    memset(b,0,sizeof(b));    h=0;    int i,j,k;    for (i=0;i<n;i++)        for (j=i+1;j<n;j++){            pwx t=jfc(a[i+1],a[j+1]);            if (!t.ex) continue;            int tmp=0;            for (k=0;k<n;k++)                if (contain(t,a[k+1])) tmp|=1<<k,b[1<<k]=1;            if (!b[tmp]) b[tmp]=1,Q[++h]=tmp;        }    for (i=0;i<n;i++)        if (!b[1<<i]) Q[++h]=1<<i;}void solve(){    init();    int i;    memset(b,0,sizeof(b));    dp[0]=0;    G.push(0);    while (!G.empty()){        int x=G.front(); G.pop();        for (i=1;i<=h;i++){            int t=x|Q[i];            if (!b[t]){                b[t]=1;                dp[t]=dp[x]+1;                G.push(t);            }        }    }    printf("%d\n",dp[(1<<n)-1]);}int main(){    int Case,i;    scanf("%d",&Case);    while (Case--){        scanf("%d%d",&n,&m);        for (i=1;i<=n;i++) scanf("%lf%lf",&a[i].x,&a[i].y);        solve();    }    return 0;}

Date:2017/10/30
By CalvinJin

原创粉丝点击