您的位置:首页 > 其它

noip模拟题10.20

2016-10-21 11:49 288 查看
啊又有很久没有写博客了,好吧事实上昨天才写了这篇,结果被csdn吃了一大半,只有今天重写。这次的题很有价值,所以抽时间把博客发了。

t1 计算几何

题意描述

花花对计算几何有着浓厚的兴趣。他经常对着平面直角坐标系发呆,思考一些有趣的问题。今天,他想到了一个十分有意思的题目:首先,花花会在x 轴正半轴和y 轴正半轴分别挑选n 个点。随后,他将x 轴的点与y 轴的点一一连接,形成n 条线段,并保证任意两条线段不相交。花花确定这种连接方式有且仅有一种。最后,花花会给出m 个询问。对于每个询问,将会给定一个点P(xp; yp),问线段OP(O 为坐标原点)与n 条线段会产生多少个交点?

输入格式

第1 行包含一个正整数n,表示线段的数量;

第2 行包含n 个正整数,表示花花在x 轴选取的点的横坐标;

第3 行包含n 个正整数,表示花花在y 轴选取的点的纵坐标;

第4 行包含一个正整数m,表示询问数量;

随后m 行,每行包含两个正整数xp 和yp,表示询问中给定的点的横、纵坐标。

输出格式

共m 行,每行包含一个非负整数,表示你对这条询问给出的答案。

样例输入

3

4 5 3

3 5 4

2

1 1

3 3

样例输出

0

3

样例解释

3 条线段分别为:(3; 0) - (0; 3)、(4; 0) - (0; 4)、(5; 0) - (0; 5)

(0; 0) - (1; 1) 不与他们有交点,答案为0。

(0; 0) - (3; 3) 与三条线段均有交点,答案为3。

数据规模与约定

• 对于40% 的数据:n;m  10;

• 另有20% 的数据:n;m  100;

• 另有20% 的数据:n;m  1000;

• 对于100% 的数据:n;m  1e5; 1  x; y < 231。

简单二分,思路应该很好想到。不过要注意二分的边界问题。我常用的模板是:

while(l<r)
{
…………
}
printf("%d",r);


不知为何反正这样不会错。

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define ll long long
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
ll a[100001],b[100001];
int main()
{
freopen("geometry.in","r",stdin);
freopen("geometry.out","w",stdout);
int m;
scanf("%d",&m);
for(int i=1;i<=m;i++)
scanf(AUTO,&a[i]);
for(int i=1;i<=m;i++)
scanf(AUTO,&b[i]);
sort(a+1,1+m+a);
sort(b+1,1+m+b);
int q;
scanf("%d",&q);
while(q--)
{
int s=1,t=m;
ll x,y;
scanf(AUTO,&x);
scanf(AUTO,&y);
while(s<=t)
{
int mid=(s+t)>>1;
if(x*b[mid]+y*a[mid]-a[mid]*b[mid]>0)
s=mid+1;
else if(x*b[mid]+y*a[mid]-a[mid]*b[mid]<0)
t=mid-1;
else
{
t=mid;
break;
}
}
printf("%d\n",t);
}
return 0;
}


花花的聚会

题意描述

花花住在H 国。H 国有n 个城市,其中1 号城市为其首都。城市间有n �� 1 条单向道路。从任意一个城市出发,都可以沿着这些单向道路一路走到首都。事实上,从任何一个城市走到首都的路径是唯一的。过路并不是免费的。想要通过某一条道路,你必须使用一次过路券。H 国一共有m 种过路券,每张过路券以三个整数表示:v k w:你可以在城市v 以价格w 买到一张过路券。这张券可以使用k 次。这意味着,拿着这张券通过了k 条道路之后,这张券就不能再使用了。请注意你同一时间最多只能拥有最多一张过路券。但你可以随时撕掉手中已有的过路券,并且在所在的城市再买一张。花花家在首都。他有q 位朋友,他希望把这些朋友们都邀请到他家做客。所以他想要知道每位朋友要花多少路费。他的朋友们都很聪明,永远都会选择一条花费最少的方式到达首都。花花需要准备晚餐去了,所以他没有时间亲自计算出朋友们将要花费的路费。你可以帮帮他么?

输入格式

输入的第一行包含两个空格隔开的整数n 和m,表示H 国的城市数量和过路券的种数。之后的n-1行各自包含两个数ai 和bi,代表城市ai 到城市bi 间有一条单向道路。

之后的m 行每行包括三个整数vi; ki 和wi,表示一种过路券。

下一行包含一个整数q,表示花花朋友的数量。

之后的q 行各自包含一个整数,表示花花朋友的所在城市。

输出格式

输出共q 行,每一行代表一位朋友的路费。

样例输入

7 7

3 1

2 1

7 6

6 3

5 3

4 3

7 2 3

7 1 1

2 3 5

3 6 2

4 2 4

5 3 10

6 1 20

3

5

6

7

样例输出

10

22

5

样例解释

对于第一位朋友,他在5 号城市只能购买一种过路券,花费10 元并且可以使用3 次。这足够他走到首都,因此总花费是10 元。对于第二位朋友,他在6 号城市只能购买20 元的过路券,并且只能使用一次。之后,他可以在3 号城市购买2 元,可以使用3 次的过路券走到首都。总花费是22 元。对于第三位朋友,他在7 号城市可以购买两种过路券。他可以花3 元买一张可以使用2次的券,然后在3 号城市再买一张2 元,可以使用3 次的券,走到首都。总花费是5 元,而且

其他的购买方式不会比这种更省钱。

数据规模与约定

• 对于40% 的数据:n; m; q  10;wi  10;

• 另有20% 的数据:n; m; q  500;wi  100;

• 另有20% 的数据:n; m; q  5000;wi  1000;

• 对于100% 的数据:n; m; q  1e5;wi  10000; 1  vi; ki  n。

考试时直接码暴力,过了6个点。想过是动归,dp[i]表示 i 城市到达根节点的最小代价,枚举 i 城市的过路劵,在 i -该过路劵所能到达的城市中,dp[i]=dp[u]+w,w为该过路劵的价格。

方程应该很好理解,那么我们看数据:n、m、q极值到了十的五次方,直接枚举肯定是过不了的,这时就要考虑倍增。

从根节点向下dp,记一个数组dis[i][j]表示 i 节点向上跳2^j步中dp的最小值,如下图:

for(int i=1;i<
f87e
/span><=16;i++)
{
fa[k][i]=fa[fa[k][i-1]][i-1];
dis[k][i]=min(dis[k][i-1],dis[fa[k][i-1]][i-1]);
}


然后就可以实现dp的方程(用邻接链表储存每个城市的过路劵)

while(Tov[t])
{
int step=Tov[t];
int w=cost[t];
int minx=0x7f7f7f7f;
int pos=k;
for(int i=0;i<=16;i++)
{
if(step&(1<<i))//保证跳到step步
{
minx=min(minx,dis[pos][i]);
pos=fa[pos][i];
}
}
dp[k]=min(dp[k],minx+w);
t=Nex[t];
}


代码如下:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int tot,cnt,head[100001],Head[100001],Tov[100001],tov[200001];
int dp[100001],nex[200001],Nex[100001];
int fa[100001][20],dis[100001][20],cost[100001];
void add(int a,int b)
{
tov[++tot]=b;
nex[tot]=head[a];
head[a]=tot;
}
void ticketadd(int a,int b,int c)
{
Tov[++cnt]=b;
cost[cnt]=c;
Nex[cnt]=Head[a];
Head[a]=cnt;
}
void dfs(int k,int FA)
{
dis[k][0]=dp[FA];
fa[k][0]=FA;
for(int i=1;i<=16;i++)
{
fa[k][i]=fa[fa[k][i-1]][i-1];
dis[k][i]=min(dis[k][i-1],dis[fa[k][i-1]][i-1]);
}
int t=Head[k];
while(Tov[t]) { int step=Tov[t]; int w=cost[t]; int minx=0x7f7f7f7f; int pos=k; for(int i=0;i<=16;i++) { if(step&(1<<i))//保证跳到step步 { minx=min(minx,dis[pos][i]); pos=fa[pos][i]; } } dp[k]=min(dp[k],minx+w); t=Nex[t]; }
t=head[k];
while(tov[t])
{
if(tov[t]!=FA)
dfs(tov[t],k);
t=nex[t];
}
}
int main()
{
freopen("party.in","r",stdin);
freopen("party.out","w",stdout);
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
}
for(int i=1;i<=m;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
ticketadd(a,b,c);
}
memset(dp,0x7f7f7f7f,sizeof dp);
memset(dis,0x7f7f7f7f,sizeof dis);
dp[1]=0;
dfs(1,0);
int q;
scanf("%d",&q);
while(q--)
{
int a;
scanf("%d",&a);
printf("%d\n",dp[a]);
}
}


t3 文本编辑器

题意描述

九发明了一个完美的文本编辑器。这个编辑器拥有两个光标(cursor),所以九能够同时在两处地方插入和删除文本。这个编辑器除了正常的编辑功能以外,还有一些只有九才知道用处的功能,例如翻转两个光标之间的文本。某一天,九把自己的完美文本编辑器给弄丢了,但是她还有好多好多文本需要处理。于是她想请聪明又智慧的你帮她实现完美文本编辑器的一些功能。

功能列表如下:

功能名称命令格式说明

‘<’ (move left) ‘< w’

w 为一个字符,“L”或“R”,表示左光标还是右光标(下同)。

该命令将选定光标向左移动,如果已经是最左端则不移动。

命令执行成功时输出“T”,若光标已经在最左端,则输出“F”。

‘>’(move right) ‘> w’

w 同上。

与< 命令不同的是,该命令将光标向右移动。

命令执行成功时输出“T”,若光标已经在最右端,则输出“F”。

‘I’ (insert) ‘I w c’

w 同上。

c 是一个可见字符(33 ascii 码 126),代表在该光标左侧插入该字符。

该命令始终输出“T”。

‘D’ (delete) ‘D w’

w 同上。

代表删除该光标右侧的一个字符。

命令执行成功时输出“T”,若光标右侧没有字符输出“F”。

‘R’ (reverse) ‘R’

代表翻转左光标和右光标之间的字符。

该命令只有左光标在右光标左侧时才能执行。

(两光标重合时也不能执行)

命令执行成功时输出“T”,否则输“F”。

‘S’ (show) ‘S’ 代表显示当前处理的文本。

该命令只输出文本,不输出“T”和“F”。

开始时文本编辑器中有一定内容,左光标在第一个字符左,右光标在最后一个字符右。

注意:在插入和删除操作中,没有被操作的光标与文本的相对左右位置保持不变。特别地,若两个光标重叠,操作后也仍然重叠。

输入格式

第一行是初始时文本编辑器内容。

6

NOIP 模拟Day1 5. 文本编辑器

第二行是一个正整数N,N 表示操作次数。

接下来有N 行,每行有一个命令,命令格式如上方表格。

输出格式

对于每个命令,按上方表格要求执行并输出。

样例输入

goodykc

11

I R u

I R l

L

L

L

L

R

D R

< R

D R

S

样例输出

T

T

T

T

T

T

T

F

T

T

goodluck

样例解释

[goodykc]

[goodykcu]

[goodykcul]

g[oodykcul]

go[odykcul]

goo[dykcul]

good[ykcul]

good[lucky]

good[lucky](光标右边没有字符,失败删除)

good[luck]y

good[luck]

goodluck

数据规模与约定

• 对于40% 的数据:1  N , 初始文本长度 100,数据不包含翻转(Reverse)操作;

• 另有30% 的数据:1  N , 初始文本长度 1e5,数据不包含翻转(Reverse)操作;

• 另有20% 的数据:1  N , 初始文本长度 1e5,数据包含翻转(Reverse)操作;

• 对于100% 的数据:1  N , 初始文本长度 4×1e6,输出文件大小 20MB;

这道题的话…考试我还是建议码暴力,因为想不出正解或者想出了正解但代码难度较大,有限时间内无法保证正确率。(我当然码的暴力…)

这道题用链表做,就是那啥pre啊next啊什么的,这算是我第一次接触这类题目。链表的思路很好理解,但难以理解的是reserve操作。每次reserve操作,我们只把光标之间最近的两个交换

a|dafdss|f 只交换a、d、s、f的前驱后继,中间的在进行其他操作的时候交换前驱后继。

代码:

#include<queue>
#include<cstdio>
#include<iostream>
#include<cstring>
#define val(x) gg[x].val
#define pre(x) gg[x].pre
#define nex(x) gg[x].nex
using namespace std;
queue<int>q;
const int maxn=1e7+5;
char T[maxn];
struct node{
int pre,nex;
char val;
inline void clear() {pre=nex=0; val='\0';}
}gg[maxn];
int con[2],tmp[2],dex,begin,end;
inline void recycle(int x)
{
q.push(x);
gg[x].clear();
}
inline int require()
{
if(!q.empty())
{
int t=q.front();
q.pop();
return t;
}
return ++dex;
}
inline char read()
{
int cc=getchar();
while(cc==' '||cc=='\n')cc=getchar();
return cc;
}
inline void lmove(int p)
{
if(tmp[p]==begin)
{
putchar('F');
return ;
}
int u=tmp[p],v=pre(u);
if(nex(v)^u)swap(pre(v),nex(v));
tmp[p]=v;con[p]--;
putchar('T');
}
inline void rmove(int p)
{
if(nex(tmp[p])==end)
{
putchar('F');
return ;
}
int u=nex(tmp[p]),v=nex(u);
if(pre(v)^u)swap(pre(v),nex(v));
tmp[p]=u;con[p]++;
putchar('T');
}
inline void insert(int p,char cc)
{
int u=tmp[p],v=nex(u);
int t=require();
pre(t)=u;nex(t)=v;
nex(u)=t;pre(v)=t;
val(t)=cc;
if(con[p^1]>=con[p])con[p^1]++;
tmp[p]=t;con[p]++;
if(tmp[p^1]==u)tmp[p^1]=t;
putchar('T');
}
inline void Delete(int p)
{
if(nex(tmp[p])==end)
{
putchar('F');
return ;
}
int u=tmp[p],v=nex(u),w=nex(v);
if(pre(w)^v)swap(nex(w),pre(w));
recycle(v);
nex(u)=w;pre(w)=u;
if(con[p^1]>con[p])con[p^1]--;
if(tmp[p^1]==v)tmp[p^1]=u;
putchar('T');
}
inline void reserve()
{
if(con[0]>=con[1])
{
putchar('F');
return ;
}
if(con[0]+1==con[1])
{
putchar('T');
return ;
}
int p1=tmp[0],p2=nex(p1),p3=tmp[1],p4=nex(p3);
swap(pre(p2),nex(p2));swap(pre(p3),nex(p3));
nex(p1)=p3;pre(p4)=p2;
nex(p2)=p4;pre(p3)=p1;
tmp[1]=p2;
putchar('T');
}
inline void show()
{
int root=begin;
while(nex(root)!=end)
{
if(pre(nex(root))^root)swap(pre(nex(root)),nex(nex(root)));
root=nex(root);
putchar(val(root));
}
}
int main()
{
freopen("editor.in","r",stdin);
freopen("editor.out","w",stdout);
scanf("%s",T);
int len=strlen(T);
begin=1;end=2;dex=2;
for(int i=0;i<len;i++)
{
val(++dex)=T[i];
pre(dex)=dex-1;
nex(dex)=dex+1;
}
pre(begin)=-1;nex(begin)=3;
pre(end)=dex;nex(end)=-1;
pre(3)=begin;nex(dex)=end;
tmp[0]=1;tmp[1]=dex;
con[0]=0;con[1]=len;
int q;
scanf("%d",&q);
while(q--)
{
char op=read();
char tt;
switch(op)
{
case '<':lmove(read()=='R');break;
case '>':rmove(read()=='R');break;
case 'I':tt=read();insert(tt=='R',read());break;
case 'D':Delete(read()=='R');break;
case 'R':reserve();break;
case 'S':show();break;
}
putchar('\n');
}
return 0;
}


第三题价值较大,注意复习
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: