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

第一题 斯诺克

	考虑这样一个斯诺克球台,它只有四个袋口,分别在四个角上(如下图所示)。我们把所有桌子边界上的整数点作为击球点(除了4个袋口),在每个击球点我们可以以45度角击球。
每一个击球点你都可以向两个方向击球,例如像下图所示.

  从S点击球有两种路线。提供桌子的尺寸,你的任务是计算出有多少种不同的击球方式使得球能入袋。球可视为质点,且无任何阻力,反弹时无能量损失。

  一个简单的证明:当一个球的运动轨迹形成一个环时,那么它必定不会到达四个角。所以对于从一个角开始运动的球,最后必定会进入其他角(唯一的路径)。如此一来,就少了很多情况,枚举从四个角开始,直到到达终点(过程略微繁琐)。

#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 N=100005;
int n,m,ans;
struct data { int w,x; };
bool vis[4][N];
data Q[N<<2]; data run(data X,int dir)
{
data Y;
if(dir==0)
{
if(X.w==2)
{
if(X.x<=n)
{
Y.w=1; Y.x=n-X.x;
if(Y.x==n) Y.w=2,Y.x=0;
} else
{
Y.w=0; Y.x=m+n-X.x;
if(Y.x==m) Y.w=1,Y.x=0;
}
} else
if(X.w==3)
{
Y.w=0; Y.x=n-X.x;
if(Y.x==m) Y.w=1,Y.x=0;
}
} else
if(dir==1)
{
if(X.w==0)
{
if(m-X.x<n)
{
Y.w=1; Y.x=m-X.x;
if(Y.x==n) Y.w=2,Y.x=0;
} else
{
Y.w=2; Y.x=m-X.x-n;
if(Y.w==m) Y.w=3,Y.x=0;
}
} else
if(X.w==3)
{
Y.w=2; Y.x=m-X.x;
if(Y.x==m) Y.w=3,Y.x=0;
}
} else
if(dir==2)
{
if(X.w==0)
{
if(X.x<=n)
{
Y.w=3; Y.x=n-X.x;
if(Y.x==n) Y.w=0,Y.x=0;
} else
{
Y.w=2; Y.x=m+n-X.x;
if(Y.w==m) Y.w=3,Y.x=0;
}
} else
if(X.w==1)
{
Y.w=2; Y.x=n-X.x;
if(Y.x==m) Y.w=3,Y.x=0;
}
} else
if(dir==3)
{
if(X.w==2)
{
if(m-X.x<n)
{
Y.w=3; Y.x=m-X.x;
if(Y.x==n) Y.w=0,Y.x=0;
} else
{
Y.w=0; Y.x=m-X.x-n;
if(Y.w==m) Y.w=1,Y.x=0;
}
} else
if(X.w==1)
{
Y.w=0; Y.x=m-X.x;
if(Y.x==m) Y.w=1,Y.x=0;
}
}
return Y;
} void bfs(int ww,int xx)
{
data fro,now;
fro.w=ww; fro.x=xx;
vis[fro.w][fro.x]=1; bool ff=1;
for(;ff;)
{
ff=0;
for(int i=0;i<4;i++)
{
if((i!=(fro.w+1)%4) && (i!=(fro.w+2)%4)) continue;
now=run(fro,i);
if(vis[now.w][now.x]) continue;
ans++; vis[now.w][now.x]=1; fro=now; ff=1;
}
}
} int main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
scanf("%d%d",&n,&m);
if(n>m) swap(n,m);
vis[0][0]=vis[1][0]=vis[2][0]=vis[3][0]=1;
bfs(0,0); bfs(1,0); bfs(2,0); bfs(3,0);
printf("%d\n",(ans<<1));
return 0;
}

第二题 完美排列

	排列,相信大家都很熟悉了,给你0 到N-1,共 N个数, 那么在排列中,每个数都要出现,而且只出现一次. 对于一个排列A, 有一个与之对应的孩子序列B, 其中B这样定义的:
1、B[0] = 0
2、B[i] = A[B[i-1]], 1 <= i <= N-1.
对于一个排列X, 如果它的孩子序列也是一个排列, 那么X称为“完美排列”。
看下面的例子,N=3时,有6个排列,其中{1, 2, 0}、{2, 0, 1}这两个排列的孩子序列也是一个排列,所有{1, 2, 0}、{2, 0, 1}是完美排列。

	给你一个整数N,再给出它的一个排列P,要你找一个完美排列Q,使得Q跟P的差距dif最小,两个排列的差距dif是这样定义的:对于每个下标i,如果P[i] 不等于 Q[i],那么dif加1, 0 <= i < N; 如果Q不唯一,那么请输出孩子序列中字典序最小的Q. 也就是在所有满足题意的完美序列中,看哪个完美序列的孩子序列字典序最小,输出该完美序列。

  题目给出的排列,可以看成是i->p[i]的边,构成若干个环,现在要把它们并成一个环,求最小的修改次数,孩子序列字典序小的优先。(一开始理解为序列的字典序最小,WA了好多次)

  简单理解,有N个环时,一定要修改N条边,N=1除外。字典序最小可以用贪心来做。先找出x=0所在的环,判断是否存在Q使得Q<P[x]且Q不与x在同一个块上。若存在,则Q所在的块与x所在的块连通,x=链的尾端(循环)。若不存在,则判断x是否为块的尾端,不是则x=p[x],否则找一个最小的Q与之相连,x=链的尾端(循环)。

  这样找出来的一定是孩子序列字典序最小的。

#include<cstdio>
#include<algorithm>
#include<cmath> #define imax(a,b) ((a>b)?(a):(b))
#define imin(a,b) ((a<b)?(a):(b))
#define Q(A,B) q[A][B] typedef long long ll; using namespace std; int ng,n,d[56];
int cnt,q[56][56];
int bb[56],fa[56];
bool vis[56],fo[56],fi[56]; int father(int x) { return (fa[x]<0?x:fa[x]=father(fa[x])); } void uni(int x,int y)
{
int fx=father(x),fy=father(y);
if(fa[fx]>fa[fy]) swap(fx,fy);
fa[fx]+=fa[fy]; fa[fy]=fx;
} int main()
{
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
scanf("%d",&ng);
while(ng--)
{
scanf("%d",&n); cnt=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&d[i]);
d[i]++; vis[i]=0;
fo[i]=fi[i]=bb[i]=0;
}
for(int i=1;i<=n;i++)
if(!vis[i])
{
fi[i]=1; fo[i]=1; vis[i]=1;
q[++cnt][0]=1; q[cnt][1]=i; bb[i]=cnt;
int x=d[i];
while(!vis[x])
{
q[cnt][++Q(cnt,0)]=x;
vis[x]=1; bb[x]=cnt; x=d[x];
}
}
int need=0;
for(int i=1;i<=cnt;i++) fa[i]=-1;
for(int g=1;1;)
if(fo[g])
{
if(need==cnt-1) break;
bool go=0; int cc;
fo[g]=0; fi[g]=0;
if(d[g]!=-1)
{
for(cc=1;cc<d[g];cc++)
if(fi[cc] && father(bb[g])!=father(bb[cc])) { go=1; break; }
if(!go)
{
if(Q(bb[g],Q(bb[g],0))!=g) { fo[d[g]]=1; g=d[g]; continue; }
for(cc=d[g];cc<=n;cc++)
if(fi[cc] && father(bb[g])!=father(bb[cc])) { go=1; break; }
}
} else
{
for(cc=1;cc<=n;cc++)
if(fi[cc] && father(bb[g])!=father(bb[cc])) { go=1; break; }
}
if(!go) break;
fo[g]=fi[g]=0; need++;
fo[cc]=fi[cc]=0;
fi[d[g]]=1; fo[d[g]]=0;
uni(bb[g],bb[cc]);
int last;
for(int i=1;i<=Q(bb[cc],0);i++)
if(d[Q(bb[cc],i)]==cc)
{
fo[Q(bb[cc],i)]=1; fi[Q(bb[cc],i)]=0;
last=Q(bb[cc],i);
d[Q(bb[cc],i)]=-1; break;
}
d[g]=cc;
g=last;
}
if(cnt>1)
{
int a,b;
for(a=1;a<=n;a++) if(fo[a]==1) break;
for(b=1;b<=n;b++) if(fi[b]==1) break;
d[a]=b;
}
for(int i=1;i<=n;i++) printf("%d ",d[i]-1); printf("\n");
}
return 0;
}

第三题 拼图

	FJ最近很烦恼,因为他正在寻找一些拼图块,这些拼图块其实可以拼成N个有顺序的完整的拼图。每个完整的拼图由若干个拼图块组成。
FJ希望把这些完整的拼图按拼出的顺序划分成M个集合,一个拼图集合由若干个完整的拼图组成,并且每个集合总的拼图块的数目不超过T。
并且,构成集合的拼图是不能交叉的,例如,当拼图1与拼图3被放在拼图集合1中之后,拼图2就只能放进拼图集合1或者不放进任何拼图集合。
FJ要找出划分成M个集合后,M个集合中最多能有多少个完整的拼图。

  一个很简单的二元组的动态规划(好像之前做过类似的题目)。

  F[i][j]=(A,B) 表示到了 i 这个物品,选了 j 个,所用的最小体积(A个拼图块,最后一个用了B的空间)

  f[i][j]=imin(f[i][j],imin(f[i-1][j],add(f[i-1][j-1],d[i])));

#include<cstdio>
#include<algorithm>
#include<cmath> #define Amax(a,b) ((a>b)?(a):(b)) typedef long long ll; using namespace std; const int N=1000;
int n,m,T,ans;
int d[N+100];
struct data
{
int x,y;
data(){}
data(int x,int y):x(x),y(y) {}
};
data f[N+100][N+100]; data add(data X,int v)
{
X.y+=v;
if(X.y>T) X.y=v,X.x++;
return X;
} data imin(data X,data Y)
{
if(X.x==Y.x) return ((X.y<Y.y)?X:Y);
else return ((X.x<Y.x)?X:Y);
} int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
scanf("%d%d%d",&n,&m,&T);
for(int i=1;i<=n;i++) scanf("%d",&d[i]);
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++) f[i][j]=data(m+1,0);
f[0][0]=data(1,0); ans=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
f[i][j]=imin(f[i][j],imin(f[i-1][j],add(f[i-1][j-1],d[i])));
//printf("%d %d %d %d\n",i,j,f[i][j].x,f[i][j].y);
if(f[i][j].x<=m) ans=Amax(ans,j);
}
}
printf("%d\n",ans);
return 0;
}

NOIP2017提高组模拟赛 7(总结)的更多相关文章

  1. NOIP2017提高组 模拟赛15(总结)

    NOIP2017提高组 模拟赛15(总结) 第一题 讨厌整除的小明 [题目描述] 小明作为一个数学迷,总会出于数字的一些性质喜欢上某个数字,然而当他喜欢数字k的时候,却十分讨厌那些能够整除k而比k小的 ...

  2. NOIP2017提高组 模拟赛13(总结)

    NOIP2017提高组 模拟赛13(总结) 第一题 函数 [题目描述] [输入格式] 三个整数. 1≤t<10^9+7,2≤l≤r≤5*10^6 [输出格式] 一个整数. [输出样例] 2 2 ...

  3. NOIP2017提高组模拟赛 10 (总结)

    NOIP2017提高组模拟赛 10 (总结) 第一题 机密信息 FJ有个很奇怪的习惯,他把他所有的机密信息都存放在一个叫机密盘的磁盘分区里,然而这个机密盘中却没有一个文件,那他是怎么存放信息呢?聪明的 ...

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

    NOIP2017提高组模拟赛 8(总结) 第一题 路径 在二维坐标平面里有N个整数点,Bessie要访问这N个点.刚开始Bessie在点(0,0)处. 每一步,Bessie可以走到上.下.左.右四个点 ...

  5. NOIP2017提高组模拟赛 9 (总结)

    NOIP2017提高组模拟赛 9 (总结) 第一题 星星 天空中有N(1≤N≤400)颗星,每颗星有一个唯一的坐标(x,y),(1≤x,y ≤N).请计算可以覆盖至少K(1≤K≤N)颗星的矩形的最小面 ...

  6. NOIP2017提高组模拟赛5 (总结)

    NOIP2017提高组模拟赛5 (总结) 第一题 最远 奶牛们想建立一个新的城市.它们想建立一条长度为N (1 <= N <= 1,000,000)的 主线大街,然后建立K条 (2 < ...

  7. NOIP2017提高组模拟赛4 (总结)

    NOIP2017提高组模拟赛4 (总结) 第一题 约数 设K是一个正整数,设X是K的约数,且X不等于1也不等于K. 加了X后,K的值就变大了,你可以重复上面的步骤.例如K= 4,我们可以用上面的规则产 ...

  8. 计蒜客NOIP2017提高组模拟赛(三)day1

    火山喷发 火山喷发对所有附近的生物具有毁灭性的影响.在本题中,我们希望用数值来模拟这一过程. 在环境里有 n 个生物分别具有 A​1​​,A​2​​,⋯,A​n​​点生命值,一次火山喷发总计 MM 轮 ...

  9. 计蒜客NOIP2017提高组模拟赛(四)day1

    T1:小X的质数 小 X 是一位热爱数学的男孩子,在茫茫的数字中,他对质数更有一种独特的情感.小 X 认为,质数是一切自然数起源的地方. 在小 X 的认知里,质数是除了本身和 1 以外,没有其他因数的 ...

随机推荐

  1. MMORPG大型游戏设计与开发(server 游戏场景 事件)

    游戏截图 场景事件 一个完整的对象一般都拥有事件,至于什么是事件在这里就不多解释了. 在场景中的事件在天龙/武侠世界中的事件包含场景初始化.场景定时器.场景退出.玩家进入场景.角色升级.角色死亡.角色 ...

  2. Swift - 制作一个在线流媒体音乐播放器(使用StreamingKit库)

    在之前的文章中,我介绍了如何使用 AVPlayer 制作一个简单的音乐播放器(点击查看1.点击查看2).虽然这个播放器也可以播放网络音频,但其实际上是将音频文件下载到本地后再播放的. 本文演示如何使用 ...

  3. Hadoop 三剑客之 —— 分布式文件存储系统 HDFS

    一.介绍 二.HDFS 设计原理     2.1 HDFS 架构     2.2 文件系统命名空间     2.3 数据复制     2.4 数据复制的实现原理     2.5 副本的选择     2 ...

  4. Vue读书笔记:关于$ref、props和$emit

    1.props实现父组件向子组件传递数据 子组件可以通过props接收到来自父组件的数据,并且是单向绑定的.也就是说,数据不能从子组件反向传递. 2.$ref实现子组件向父组件通信 来自官方非常难理解 ...

  5. vue.js和node.js的认识

    首先vue.js 是库,不是框架,不是框架,不是框架. Vue.js 使用了基于 HTML 的模版语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据. Vue.js 的核心是一个允许你 ...

  6. Excel里的多列求和(相邻或相隔皆适用)

    最近,需要这个知识点,看到网上各种繁多复杂的资料,自己梳理个思路. 不多说,直接上干货! 简述:将L列.M列和N列,相加放到O列.(当然这里是相邻的列).同时,也可以求相隔几列的某些列相加.

  7. JVM源码分析之javaagent原理完全解读--转

    原文地址:http://www.infoq.com/cn/articles/javaagent-illustrated 概述 本文重点讲述javaagent的具体实现,因为它面向的是我们Java程序员 ...

  8. dedecms清空栏目后,新建ID不从1开始的解决方法

    在后台SQL运行器运行下面的语句,这样新建的栏目ID就从1开始了: ALTER TABLE `dede_arctype` AUTO_INCREMENT =1; (注意表名) 下面是文章的,运行后,发布 ...

  9. 洛谷1613 跑路 倍增 + Floyd

    首先,我们一定要认识到本题中的最短时间所对应的道路不一定是在起点到终点的最短路.例如,起点到终点的最短路为 151515 ,那么对 151515 进行二进制拆分的话是 111111111111 ,这时 ...

  10. (1)安装vagrant和virtualbox

    使用xshell,学校服务器需要先联外网. 1.安装Linux头包(linux-header package): # #yum install kernel-devel 2.安装virtualbox. ...