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