NOIP2017提高组模拟赛 8(总结)

来源:互联网 发布:淘宝卖爱奇艺会员 编辑:程序博客网 时间:2024/05/22 06:35

NOIP2017提高组模拟赛 8(总结)

第一题 路径

  一个定理,假如点i到点j有一条路径是偶数,那么其他路径也一定是偶数(最短距离需要的上下左右是固定的,多余的上和下抵消,多余的左和右抵消),反之亦然。
  简单的证明,假如需要的是偶数,那么只需判断是否存在一个点到(0,0)的距离是偶数就行了(作为终点),其它点可以从(0,0)出发到(xi,yi)再回到(0,0),这样肯定为偶数步数。需要的是奇数的也类似。

#include<cstdio>#include<algorithm>#include<cmath>#define imax(a,b) ((a>b)?(a):(b))#define imin(a,b) ((a<b)?(a):(b))typedef long long ll;using namespace std;int ng,n,ne,a,b,s[5];int main(){    freopen("a.in","r",stdin);    freopen("a.out","w",stdout);    scanf("%d",&ng);    while(ng--)    {        scanf("%d%d",&n,&ne);        s[0]=s[1]=0;        for(int i=1;i<=n;i++)        {            scanf("%d%d",&a,&b);            int c=abs(a)+abs(b);            s[c&1]++;        }        if(ne==0)        {            if(s[0]>0) printf("CAN\n"); else printf("CANNOT\n");        } else        if(ne==1)        {            if(s[1]>0) printf("CAN\n"); else printf("CANNOT\n");        }    }    return 0;}

第二题 冠军

  已知N一定是2的若干次幂,而且不超过16,也就是说N是{2,4,8,16}之中的某一个数。
  现在的问题是:有多少种不同的分配方案,使得第i个选手能最终成为冠军?不妨假设该数值是ans[i]。
  你的任务就是输出:ans[0]、ans[1]、….ans[N-1]。
  一道状压DP题,不过要注意优化,过多的无用状态没过滤掉会被卡。
  若beat[i][j]==’Y’  F[S][i]+=F[S1][i]*F[S-S1][j]
  若beat[i][j]==’N’  F[S][j]+=F[S1][i]*F[S-S1][j]
  F[S][i]表示选了S这几个拳手(2进制,0为不选,1为选),最终胜利的拳手为i的方案数。
  结果会爆INT,要用LONG LONG来存。
  (PS:一开始,码了搜索去找状态,结果WA了,机子单步不了,又找不出哪里错。后来比赛结束重新码了程序才过。)

#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>#define imax(a,b) ((a>b)?(a):(b))#define imin(a,b) ((a<b)?(a):(b))#define pd(S,i) ((S&(1<<(i-1)))>0)typedef long long ll;using namespace std;const int MP=100500;int n,hn,nn,B[20],C[20];char st[20];bool ff[20][20];int Go[6][MP],Set[6][MP],one[MP],A[20];ll f[MP][20];int main(){    freopen("b.in","r",stdin);    freopen("b.out","w",stdout);    scanf("%d",&n); hn=n>>1;    nn=(1<<n);    for(int i=1;i<=n;i++)    {        scanf("%s",st);        for(int j=1;j<=n;j++) ff[i][j]=(st[j-1]=='Y');    }    one[0]=0;    for(int i=1;i<nn;i++) one[i]=one[i>>1]+(i&1);    for(int i=1;i<nn;i++)    {        if(one[i]==1) { Set[0][++Set[0][0]]=i; if(i<(1<<2)) Go[0][++Go[0][0]]=i; } else        if(one[i]==2) { Set[1][++Set[1][0]]=i; if(i<(1<<4)) Go[1][++Go[1][0]]=i; } else        if(one[i]==4) { Set[2][++Set[2][0]]=i; if(i<(1<<8)) Go[2][++Go[2][0]]=i; } else        if(one[i]==8) { Set[3][++Set[3][0]]=i; if(i<(1<<16)) Go[3][++Go[3][0]]=i; }    }    for(int i=1;i<=Set[0][0];i++)    {        int S=Set[0][i];        for(int j=1;j<=n;j++)        if(pd(S,j)) f[S][j]=1;    }    Set[4][0]=1; Set[4][1]=(1<<16)-1;    int go=0;    if(n==16) go=4; else    if(n==8) go=3; else    if(n==4) go=2; else go=1;    for(int ho=1;ho<=go;ho++)    {        for(int i=1;i<=Set[ho][0];i++)        {            int S=Set[ho][i],S1; A[0]=0;            for(int j=1;j<=n;j++)            if(pd(S,j)) A[++A[0]]=j;            for(int h=1;h<=Go[ho-1][0];h++)            {                int ss=Go[ho-1][h]; S1=0;                B[0]=0; C[0]=0;                for(int k=1;k<=(1<<ho);k++)                {                    if(pd(ss,k))                    {                        S1|=(1<<(A[k]-1));                        B[++B[0]]=A[k];                    } else C[++C[0]]=A[k];                }                for(int fa=1;fa<=B[0];fa++)                for(int fb=1;fb<=C[0];fb++)                if(ff[B[fa]][C[fb]]) f[S][B[fa]]+=f[S1][B[fa]]*f[S-S1][C[fb]];                else f[S][C[fb]]+=f[S1][B[fa]]*f[S-S1][C[fb]];            }        }    }    for(int i=1;i<=n;i++) printf("%I64d\n",f[nn-1][i]);    return 0;}

第三题 指纹

  这题比前一题好打多了,不过一直在调第二题,这题便没有做……
  每一个指纹有四个参数ABCD
  先考虑ABC,因为假设i的D很小,但ABC很大,那么它也会被刷掉。
  按A排序,用B作为下标,存C。用线段树维护一段区间的最小值。
  A从小到大排序,对于i来说,比它的A小的都已经加入了线段树中,若findmin(1,B)比C小,证明一定存在x的ABC均小于i,那么i也没有存在的意义了(打del标记)。
  再考虑ABD,ACD,BCD,一共四种情况,逐一筛掉无用的指纹就行了。

#include<cstdio>#include<algorithm>#include<cmath>#define imax(a,b) ((a>b)?(a):(b))#define imin(a,b) ((a<b)?(a):(b))typedef long long ll;using namespace std;const int oo=1e9;const int N=1e5;struct data { int a,b,c,d,id; } d[N+100],dg[N+100];int n;bool ffg[N+100];int tree[N<<2];bool cmp(data A,data B) { return (A.a<B.a); }void build(int ro,int L,int R){    if(L==R) { tree[ro]=oo; return; }    int Mid=(L+R)>>1;    build(ro<<1,L,Mid);  build(ro<<1|1,Mid+1,R);    tree[ro]=imin(tree[ro<<1],tree[ro<<1|1]);}void pre(int ho){    for(int i=1;i<=n;i++) dg[i]=d[i];    if(ho==1) for(int i=1;i<=n;i++) swap(dg[i].c,dg[i].d); else    if(ho==2) for(int i=1;i<=n;i++) swap(dg[i].b,dg[i].d); else    if(ho==3) for(int i=1;i<=n;i++) swap(dg[i].a,dg[i].d);    sort(dg+1,dg+1+n,cmp);    build(1,1,n);}void updata(int ro,int L,int R,int x,int val){    if(L>x || R<x) return;    if(L==x && x==R)    {        tree[ro]=val; return;    }    int Mid=(L+R)>>1;    updata(ro<<1,L,Mid,x,val); updata(ro<<1|1,Mid+1,R,x,val);    tree[ro]=imin(tree[ro<<1],tree[ro<<1|1]);}int query(int ro,int L,int R,int li,int ri){    if(L>ri || R<li) return oo;    if(li<=L && R<=ri) return tree[ro];    int Mid=(L+R)>>1;    int x1=query(ro<<1,L,Mid,li,ri),x2=query(ro<<1|1,Mid+1,R,li,ri);    return (imin(x1,x2));}int main(){    freopen("c.in","r",stdin);    freopen("c.out","w",stdout);    scanf("%d",&n);    for(int i=1;i<=n;i++) scanf("%d%d%d%d",&d[i].a,&d[i].b,&d[i].c,&d[i].d),d[i].id=i;    for(int ho=0;ho<4;ho++)    {        pre(ho);        for(int i=1;i<=n;i++)        {            int temp=query(1,1,n,1,dg[i].b);            if(temp<dg[i].c) ffg[dg[i].id]=1;            updata(1,1,n,dg[i].b,dg[i].c);        }    }    int dell=0;    for(int i=1;i<=n;i++) dell+=ffg[i];    printf("%d\n",dell);    for(int i=1;i<=n;i++)    if(ffg[i]) printf("%d\n",i);    return 0;}
原创粉丝点击