您的位置:首页 > 其它

暑假集训test8(伪)【NOIP2016提高组Day1】

2017-07-20 15:34 155 查看
本以为是考试,拿到题目的时候一脸懵。

这不是去年的NOIP考试题吗?

所以这次倒是成功的A了两道。

嗯上题。

1.玩具谜题

题目描述

小南有一套可爱的玩具小人,它们各有不同的职业。

有一天,这些玩具小人把小南的眼镜藏了起来。小南发现玩具小人们围成了一个圈,它们有的面朝圈内,有的面朝圈外。如下图:

(假装有图的样子)

这时 singer 告诉小南一个谜题:“眼镜藏在我左数第 3 个玩具小人的右数第 1 个玩具小人的左数第 2 个玩具小人那里。”

小南发现,这个谜题中玩具小人的朝向非常关键,因为朝内和朝外的玩具小人的左右方向是相反的:面朝圈内的玩具小人,它的左边是顺时针方向,右边是逆时针方向;而面向圈外的玩具小人,它的左边是逆时针方向,右边是顺时针方向。

小南一边艰难地辨认着玩具小人,一边数着:

“singer 朝内,左数第 3 个是 archer。

“archer 朝外,右数第 1 个是 thinker。

“thinker 朝外,左数第 2 个是 writer。

“所以眼镜藏在 writer 这里!”

虽然成功找回了眼镜,但小南并没有放心。如果下次有更多的玩具小人藏他的眼镜,或是谜题的长度更长,他可能就无法找到眼镜了。所以小南希望你写程序帮他解决类似的谜题。这样的谜题具体可以描述为:

有 n 个玩具小人围成一圈,已知它们的职业和朝向。现在第 1 个玩具小人告诉小南一个包含 m 条指令的谜题,其中第 i 条指令形如“左数/右数第 si 个玩具小人”。你需要输出依次数完这些指令后,到达的玩具小人的职业。

输入格式

输入的第一行包含两个正整数 n ,m ,表示玩具小人的个数和指令的条数。

接下来 n 行,每行包含一个整数和一个字符串,以逆时针为顺序给出每个玩具小人的朝向和职业。其中 0 表示朝向圈内,1 表示朝向圈外。保证不会出现其他的数。字符串长度不超过 10 且仅由小写字母构成,字符串不为空,并且字符串两两不同。整数和字符串之间用一个空格隔开。

接下来 m 行,其中第 i 行包含两个整数 ai,si ,表示第 i 条指令。若 ai = 0 ,表示向 左数 si 个人;若 ai = 1 ,表示向右数 si 个人。保证 ai 不会出现其它的数,1 ≤si

输出格式

输出一个字符串,表示从第一个读入的小人开始,依次数完 m 条指令后到达的小人的职业。

样例数据 1

输入

7 3

0 singer

0 reader

0 mengbier

1 thinker

1 archer

0 writer

1 mogician

0 3

1 1

0 2

输出

writer

样例数据 2

输入

10 10

1 c

0 r

0 p

1 d

1 e

1 m

1 t

1 y

1 u

0 v

1 7

1 1

1 4

0 5

0 3

0 1

1 6

1 2

0 8

0 4

输出

y

备注

【样例1说明】

这组数据就是【题目描述】中提到的例子。

【数据规模与约定】(没有图就什么也没有)

真的是一到比较简单的题。

0表示朝向圈内,1表示朝向圈外,0向左数,1向右数。

所以二者相同为顺时针转,不同为逆时针转。

注意转的超过一圈要%n。

用‘^’效果一样。

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;

int m,n,now;
int a[100005],b[100005],c[100005];
string s[100005];

int main()
{
//freopen("toy.in","r",stdin);
//freopen("toy.out","w",stdout);

cin>>n>>m;
for(int i=0;i<n;i++)
cin>>a[i]>>s[i];
for(int i=0;i<m;i++)
{
cin>>b[i]>>c[i];
if(a[now]^b[i]==1)
{
now+=c[i];
now%=n;
}
else
{
now+=(n-c[i]+n)%n;
now%=n;
}
}
cout<<s[now]<<endl;
return 0;
}


2.天天爱跑步

题目描述

小 C 同学认为跑步非常有趣,于是决定制作一款叫做《天天爱跑步》的游戏。《天天爱跑步》是一个养成类游戏,需要玩家每天按时上线,完成打卡任务。

这个游戏的地图可以看作一一棵包含 n 个结点和 n-1 条边的树, 每条边连接两个结点,且任意两个结点存在一条路径互相可达。树上结点编号为从 1 到 n 的连续正整数。

现在有 m 个玩家,第 i 个玩家的起点为 Si ,终点为 Ti 。每天打卡任务开始时,所有玩家在第 0 秒同时从自己的起点出发,以每秒跑一条边的速度,不间断地沿着最短路径向着自己的终点跑去, 跑到终点后该玩家就算完成了打卡任务。 (由于地图是一棵树, 所以每个人的路径是唯一的)

小 C 想知道游戏的活跃度, 所以在每个结点上都放置了一个观察员。 在结点 j 的观察员会选择在第 Wj 秒观察玩家, 一个玩家能被这个观察员观察到当且仅当该玩家在第 Wj 秒也正好到达了结点 j 。 小 C 想知道每个观察员会观察到多少人?

注意: 我们认为一个玩家到达自己的终点后该玩家就会结束游戏, 他不能等待一段时间后再被观察员观察到。 即对于把结点 j 作为终点的玩家: 若他在第 Wj 秒前到达终点,则在结点 j 的观察员不能观察到该玩家;若他正好在第 Wj 秒到达终点,则在结点 j 的观察员可以观察到这个玩家。

输入格式

第一行有两个整数 n 和 m 。其中 n 代表树的结点数量, 同时也是观察员的数量, m 代表玩家的数量。

接下来 n-1 行每行两个整数 u 和 v ,表示结点 u 到结点 v 有一条边。

接下来一行 n 个整数,其中第 j 个整数为 Wj, 表示结点 j 出现观察员的时间。

接下来 m 行,每行两个整数 Si 和 Ti ,表示一个玩家的起点和终点。

对于所有的数据,保证 1≤Si,Ti≤n,0≤Wj≤n 。

输出格式

输出 1 行 n 个整数,第 j 个整数表示结点 j 的观察员可以观察到多少人。

样例数据 1

输入

6 3

2 3

1 2

1 4

4 5

4 6

0 2 5 1 2 3

1 5

1 3

2 6

输出

2 0 0 1 1 1

样例数据 2

输入

5 3

1 2

2 3

2 4

1 5

0 1 0 3 0

3 1

1 4

5 5

输出

1 2 1 0 1

备注

【样例1说明】

对于 1 号点,W1 = 0 ,故只有起点为 1 号点的玩家才会被观察到,所以玩家 1 和玩家 2 被观察到,共有 2 人被观察到。

对于 2 号点,没有玩家在第 2 秒时在此结点,共 0 人被观察到。

对于 3 号点,没有玩家在第 5 秒时在此结点,共 0 人被观察到。

对于 4 号点,玩家 1 被观察到,共 1 人被观察到。

对于 5 号点,玩家 1 被观察到,共 1 人被观察到。

对于 6 号点,玩家 3 被观察到,共 1 人被观察到。

【数据规模与约定】(没有图就是硬伤)

咳,先上代码。

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#include<vector>
#define N 300010
using namespace std;

struct node1
{
int next,to;
}bian[N<<1];
struct node2
{
int s,t;
};
int n,m,tot;
int first
,jump
[20],deep
,w
,ans
,num[N<<2];
vector <node2> dian
;

inline void add(int x,int y)
{
tot++;
bian[tot].next=first[x];
first[x]=tot;             //树上建边不多说。
bian[tot].to=y;
}

inline void dfs(int x)
{
for(int i=1;i<=18;i++)
{
jump[x][i]=jump[jump[x][i-1]][i-1];
if(!jump[x][i])
break;
}
for(int i=first[x];i;i=bian[i].next)
if(bian[i].to!=jump[x][0])        //第一次dfs,预处理。
{
deep[bian[i].to]=deep[x]+1;
jump[bian[i].to][0]=x;
dfs(bian[i].to);
}
}

inline int lca(int a,int b)
{
if(deep[a]<deep)
swap(a,b);
int k=deep[a]-deep[b];
for(int i=18;i>=-1;i--)
if(k&(1<<i))
a=jump[a][i];
if(a==b)
return a;
for(int i=18;i>=-1;i--)
if(jump[a][i]!=jump[b][i])
{
a=jump[a][i];
b=jump[b][i];
}
return jump[a][0];
}

inline void work(int x)
{
int last=num[deep[x]+w[x]]+num[w[x]-deep[x]+n*3+1];
int k=dian[x].size();
for(int i=0;i<k;i++)
{
node2 u=dian[x][i];
num[u.s]+=u.t;
}
for(int i=first[x];i;i=bian[i].next)
if(bian[i].to!=jump[x][0])
work(bian[i].to);
ans[x]=num[deep[x]+w[x]]+num[w[x]-deep[x]+n*3+1]-last;
}

int main()
{
int _q=20<<20;
char *_p=(char*)malloc(_q)+_q;
__asm__("movl %0, %%esp\n"::"r"(_p));
int k,s,t;
node2 u;
cin>>n>>m;
for(int i=1;i<n;i++)
{
cin>>s>>t;
add(s,t);
add(t,s);
}
dfs(1);
for(int i=1;i<=n;i++)
cin>>w[i];
for(int i=1;i<=m;i++)
{
cin>>s>>t;
k=lca(s,t);

u.s=deep[s];
u.t=1;
dian[s].push_back(u);            //若有一段要加1,将那一段加1。
u.t=-1;                          //然后将其后面的减1即可。
dian[jump[k][0]].push_back(u);

u.s=deep[s]-deep[k]*2+n*3+1;
u.t=1;
dian[t].push_back(u);
u.t=-1;
dian[k].push_back(u);
}
work(1);
for(int i=1;i<=n;i++)
cout<<ans[i]<<" ";
return 0;
}


真的好了不多说,我也不怎么讲得清楚,能理解就理解。

3.换教室

[b]题目描述

对于刚上大学的牛牛来说,他面临的第一个问题是如何根据实际情况申请合适的课程。

在可以选择的课程中,有 2n 节课程安排在 n 个时间段上。在第 i(1≤i≤n)个时间段上,两节内容相同的课程同时在不同的地点进行,其中,牛牛预先被安排在教室 ci 上课,而另一节课程在教室 di 进行。

在不提交任何申请的情况下,学生们需要按时间段的顺序依次完成所有的 n 节安排好的课程。如果学生想更换第 i 节课程的教室,则需要提出申请。若申请通过,学生就可以在第 i 个时间段去教室 di 上课,否则仍然在教室 ci 上课。

由于更换教室的需求太多,申请不一定能获得通过。通过计算,牛牛发现申请更换第 i 节课程的教室时,申请被通过的概率是一个已知的实数 ki ,并且对于不同课程的申请,被通过的概率是互相独立的。

学校规定,所有的申请只能在学期开始前一次性提交,并且每个人只能选择至多 m 节课程进行申请。这意味着牛牛必须一次性决定是否申请更换每节课的教室,而不能根据某些课程的申请结果来决定其他课程是否申请;牛牛可以申请自己最希望更换教室的 m 门课程,也可以不用完这 m 个申请的机会,甚至可以一门课程都不申请。

因为不同的课程可能会被安排在不同的教室进行,所以牛牛需要利用课间时间从一间教室赶到另一间教室。

牛牛所在的大学有 v 个教室,有 e 条道路。每条道路连接两间教室,并且是可以双向通行的。由于道路的长度和拥堵程度不同,通过不同的道路耗费的体力可能会有所不同。当第 i(1≤i≤n-1)节课结束后,牛牛就会从这节课的教室出发,选择一条耗费体力最少的路径前往下一节课的教室。

现在牛牛想知道,申请哪几门课程可以使他因在教室间移动耗费的体力值的总和的期望值最小,请你帮他求出这个最小值。

输入格式

第一行四个整数 n,m,v,e 。n 表示这个学期内的时间段的数量;m 表示牛牛最多可以申请更换多少节课程的教室;v 表示牛牛学校里教室的数量;e 表示牛牛的学校里道路的数量。

第二行 n 个正整数,第 i(1≤i≤n)个正整数表示 ci ,即第 i 个时间段牛牛被安排上课的教室;保证 1≤ci≤v。

第三行 n 个正整数,第 i(1≤i≤n)个正整数表示 di ,即第 i 个时间段另一间上同样课程的教室;保证 1≤di≤v。

第四行 n 个实数,第 i(1≤i≤n)个实数表示 ki ,即牛牛申请在第 i 个时间段更换教室获得通过的概率。保证 0≤ki≤1。

接下来 e 行,每行三个正整数 aj ,bj ,wj ,表示有一条双向道路连接教室 aj ,bj ,通过这条道路需要耗费的体力值是 wj;保证 1≤aj,bj≤v,1≤wj≤100

保证 1≤n≤2000,0≤m≤2000,1≤v≤300,0≤e≤90000。

保证通过学校里的道路,从任何一间教室出发,都能到达其他所有的教室。

保证输入的实数最多包含 3 位小数。

输出格式

输出一行,包含一个实数,四舍五入精确到小数点后恰好 2 位,表示答案。你的输出必须和标准输出完全一样才算正确。

测试数据保证四舍五入后的答案和准确答案的差的绝对值不大于 4×10-3。(如果你不知道什么是浮点误差,这段话可以理解为:对于大多数的算法,你可以正常地使用浮点数类型而不用对它进行特殊的处理)

样例数据

输入

3 2 3 3

2 1 2

1 2 1

0.8 0.2 0.5

1 2 5

1 3 3

2 3 1

输出

2.80

(很好这次就是没有备注。)

题目描述很长很长很长很长……

但是可做,用dp。

先开一个二维数组f[i][j],表示当前已经考虑了i门课,提交了j门课的换教室申请的最小期望值。

但显然这是不够的,因为你不能确定那些课提交了申请。

所以再开一维[2],若为f[i][j][1],则表示当前考虑的这门课提交了申请;若为f[i][j][0],则表示当前考虑的这门课没有提交申请。

先用Floyed更新每间教室之间的距离,便于后面直接计算。

对于状态转移方程要分四种情况讨论(c[i]表示原教室,d[i]表示换课成功后的教室):

1.上节课没有递交申请,这节课也没有,则100%在原教室上课。

f[i+1][j][0]=min(f[i+1][j][0],f[i][j][0]+dis[c[i]][c[i+1]])

2.上节课没有递交申请,这节课交了,则有k[i+1]的可能性会换,有1-k[i+1]的可能性不换。

f[i+1][j+1][1]=min(f[i+1][j+1][1],f[i][j][0]+dis[c[i]][d[i+1]]×k[i+1]+dis[c[i]][c[i+1]]×(1-k[i+1]))

3.上节课交了申请,这节课未交,则有k[i]的可能上节课换了教室,有1-k[i]可能上节课没换。

f[i+1][j][0]=min(f[i+1][j][0],f[i][j][1]+dis[c[i]][c[i+1]]×(1-k[i])+dis[d[i]][c[i+1]]×k[i])

4.上节课交了申请,这节课也交了,则有(1-k[i])×(1-k[i+1])的可能二者都不换;(1-k[i])×k[i+1]的可能上节课没换,这节课换了;k[i]×(1-k[i+1])的可能上节课换了,这节课没换;k[i]×k[i+1]的可能两节课都换了。

f[i+1][j+1][1]=min(f[i+1][j+1][1],f[i][j][1]+dis[c[i]][c[i+1]]×(1-k[i])×(1-k[i+1])+dis[d[i]][c[i+1]]×k[i]×(1-k[i+1])+dis[c[i]][d[i+1]]×(1-k[i])×k[i+1]+dis[d[i]][d[i+1]]×k[i]×k[i+1]) (方程太长建议在Dev c++里看)

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;

int n,m,v,e,x,y;
int c[2003],d[2003];
double inf,z,ans;
double k[2003],dis[2003][2003],f[2003][2003][2];

int main()
{
//freopen("classroom.in","r",stdin);
memset(dis,127,sizeof(dis));
memset(f,127,sizeof(f));

cin>>n>>m>>v>>e;
for(int i=1;i<=n;i++)
cin>>c[i];
for(int i=1;i<=n;i++)
cin>>d[i];
for(int i=1;i<=n;i++)
cin>>k[i];
inf=dis[0][0];
for(int i=1;i<=v;i++)
dis[i][i]=0.0;
for(int i=1;i<=e;i++)
{
cin>>x>>y>>z;
dis[x][y]=dis[y][x]=min(dis[x][y],z);
}

for(int kk=1;kk<=v;kk++)
for(int i=1;i<=v;i++)        //这就是教室间距离的来源。
for(int j=1;j<=v;j++)
dis[i][j]=min(dis[i][j],dis[i][kk]+dis[kk][j]);

for(int i=2;i<=n;i++)
ans+=dis[c[i-1]][c[i]];
f[1][0][0]=f[1][1][1]=0.0;
for(int i=1;i<=n;i++)
for(int j=0;j<=min(i,m);j++)
{
if(f[i][j][0]!=inf)
{
f[i+1][j][0]=min(f[i+1][j][0],f[i][j][0]+dis[c[i]][c[i+1]]);
if(j<m)
f[i+1][j+1][1]=min(f[i+1][j+1][1],f[i][j][0]+dis[c[i]][d[i+1]]*k[i+1]+dis[c[i]][c[i+1]]*(1-k[i+1]));
}
if(f[i][j][1]!=inf)
{
f[i+1][j][0]=min(f[i+1][j][0],f[i][j][1]+dis[c[i]][c[i+1]]*(1-k[i])+dis[d[i]][c[i+1]]*k[i]);
if(j<m)
f[i+1][j+1][1]=min(f[i+1][j+1][1],f[i][j][1]+dis[c[i]][c[i+1]]*(1-k[i])*(1-k[i+1])+dis[d[i]][c[i+1]]*k[i]*(1-k[i+1])+dis[c[i]][d[i+1]]*(1-k[i])*k[i+1]+dis[d[i]][d[i+1]]*k[i]*k[i+1]);    //好吧依旧很长。
}
}
for(int i=0;i<=m;i++)
ans=min(ans,min(f
[i][0],f
[i][1]));
printf("%0.2lf\n",ans);
return 0;
}


就这样,其实还有test的续集。

敬请期待。

来自2017.7.19

——我认为return 0,是一个时代的终结。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  NOIP2016 提高组