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

时间:2021-04-17 03:55:59

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

第一题 路径

	在二维坐标平面里有N个整数点,Bessie要访问这N个点。刚开始Bessie在点(0,0)处。 每一步,Bessie可以走到上、下、左、右四个点。即假设Bessie当前所在点的坐标是(x,y),那么它下一步可以移动到(x,y+1), (x,y-1), (x+1,y), (x-1,y)之一。
Bessie目标是找到一个移动序列,满足下面的条件:
1、从点(0,0)出发。
2、对于给定的那N个点,每个点至少被访问一次。
3、可以选择那N个给定点中任意一个作为结束点。
现在为了增加难度,农夫规定Bessie走过的所有步数之和的奇偶性必须为wantedParity,显然wantedParity 是0或1,是题目给定的,0表示偶,1表示奇。Bessie立刻感觉到了难度的增加,如果存在满足条件的移动序列,那么输出CAN,否则输出CANNOT。

  一个定理,假如点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个拳手参加擂台赛,这个人的编号是0至N-1。有N个位置,编号从0至N-1。每个位置分配一个拳手,显然共有N!种不同的分配方案。
对于一种具体的分配方案,站在位置0的拳手与站在位置1的拳手比赛,胜者进入下一轮,败者被淘汰。站在位置2的拳手与站在位置3的拳手比赛,胜者进入下一轮,败者被淘汰,同样道理,站在位置4的拳手与站在位置5的拳手比赛,胜者进入下一轮,败者被淘汰。。。最终会产生一个冠军拳手。
如下图所示:

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

	已知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;
}

第三题 指纹

	随着科技的发展,当今很多企业向社会推出了越来越多的结合指纹识别技术的高科技产品。其中以需要进行身份验证或身份识别类型的产品居多,如门禁系统、手提电脑、指纹硬盘。
对任何生物识别系统来说,输入数据的质量对于系准确率有着重大的影响。为了获得较高质量的指纹图像,近年来指纹采集设备不断地被更新,各种先进的指纹采集技术也逐渐被引入到实际产品中。尽管如此,由于手指皮肤情况、空气湿度、灰尘等一些因素的影响,依旧存在着大量的质量不高的指纹图像。
通常我们可以通过编号为A,B,C,D的四个属性来评估一个指纹图像的质量的高低:
A为杂点的数量;B为折痕的数量;C为脊线断续程度;D为脊线粘连程度。这四个属性值越小表示该图像在相应方面表现越优。
由于指纹图质量评估研究的需要,我们通过对一个人的指纹进行多次采样后得到多个不同质量的指纹图像,并将其各质量属性记录在一个数据库中(不同图像的各属性值均不相同)。对于两个指纹图像,单个属性的好坏并不能说明图像质量的高低。比如图像1杂点数比图像2的少,但有可能图像1的粘连程度比图像2高得多,因此不能武断地认为图像1就比图像2好。
但是如果一个图像有不少于三个属性都优于另一个图像,那么我们有理由相信前者的质量要优于后者。对于数据库中的一个指纹图像I,如果存在另一个图像J,J有不少于三个质量属性优于图像I,那么我们认为图像I是一种“累赘”。
为了减少指纹图像数据库的大小,我们希望去除其中的累赘图像,现在实验室需要你的帮忙,找出该部分图像。为方便就算,我们已经分别按四个属性,计算出了所有图像按该属性的优劣程度排序的名次。

  这题比前一题好打多了,不过一直在调第二题,这题便没有做……

  每一个指纹有四个参数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;
}