【loli的胡策】NOIP训练7.15(签到+dp+线段树)
2017-07-15 14:19
337 查看
T2题目:
【问题描述】
平面内给 n
个点,记横坐标最小的点为A,最大的点为B,现在小Y想要知道在
每个点经过一次(A
点两次)的情况下从A走到B,再回到A
的最短路径。但他是个强
迫症患者,他有许多奇奇怪怪的要求与限制条件:
1.从A走到B
时,只能由横坐标小的点走到大的点。
2.由B
回到A
时,只能由横坐标大的点走到小的点。
3.有两个特殊点b1
和b2,b1
在 0
到n-1 的路上,b2
在n-1
到0 的路上。
请你帮他解决这个问题助他治疗吧!
【输入格式】
第一行三个整数n,b1,b2,(0<b1,b2<n-1
且b1<>b2)。n
表示点数,从0
到n-1 编
号,b1 和b2
为两个特殊点的编号。
以下n
行,每行两个整数 x、y
表示该点的坐标(0<=x,y<=2000),从0
号点顺序
给出。DoctorGao为了方便他的治疗,已经将给出的点按x
增序排好了。
【输出格式】
输仅一行,即最短路径长度(精确到小数点后面2
位)
【样例输入输出】
paths.in
5 1 3
1 3
3 4
4 1
7 5
8 3
paths.out
18.18
【样例解释】 最短路径:0->1->4->3->2->0
【数据范围】
20%的数据n<=20
60%的数据n<=300
100%的数据n<=1000
对于所有数据x,y,b1,b2如题目描述.
题解:
这是个dp,考场上怎么也没往这方面想。。。我们可以考虑为从1走到n两遍,求两条不相交的路径最小
为了不转移到f[i][i]就让k=max(i,j)+1
然后特殊点什么的特判就好了
代码:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
struct hh
{
int x,y;
}s[1005];
int n,b1,b2;
bool a[1005];
double f[1005][1005],minn=1000000000;
double ll(int a,int b)
{
return sqrt(double((s[a].x-s.x)*(s[a].x-s[b].x)+(s[a].y-s[b].y)*(s[a].y-s[b].y)));
}
int main()
{
freopen("paths.in","r",stdin);
freopen("paths.out","w",stdout);
int i,x,y,j;
scanf("%d%d%d",&n,&b1,&b2);b1++,b2++;
for (i=1;i<=n;i++)
scanf("%d%d",&s[i].x,&s[i].y);//f[i][j]表示第一个点走到i,第二个点走到j的最短距离
memset(f,0x7f,sizeof(f));
f[1][1]=0;
for (i=1;i<=n;i++)
for (j=1;j<=n;j++)
if (i!=j || i==1)
{
int k=max(i,j)+1;
if (k==n+1)
{
if (j==n) f
=min(f
,f[i][j]+ll(i,n));
if (i==n) f
=min(f
,f[i][j]+ll(j,n));
continue;
}
if (k!=b1) f[i][k]=min(f[i][k],f[i][j]+ll(j,k));
if (k!=b2) f[k][j]=min(f[k][j],f[i][j]+ll(i,k));
}
printf("%.2lf",f
);
}
[b]T3题目:
时间限制 : 20000MS空间限制 : 265536 KB
问题描述
刚拿到驾照的 KJ
总喜欢开着车到处兜风,玩完了再把车停到阿 Q的停车场里,虽然她对自己停车的水平很有信心,但她还是不放心其他人的停车水平,尤其是 Kelukin。于是,她每次都把自己的爱车停在距离其它车最远的一个车位。KJ觉得自己这样的策略非常科学,于是她开始想:在一个停车场中有一排车位,从左到右编号为
1到 n,初始时全部是空的。有若干汽车,进出停车场共 m次。对于每辆进入停车场的汽车,会选择与其它车距离最小值最大的一个车位,若有多个符合条件,选择最左边一个。KJ想着想着就睡着了,在她一旁的
Kelukin想帮她完成这个心愿,但是他又非常的懒,不愿意自己动手,于是就把这个问题就留给了你:在 KJ
理想的阿 Q
的停车场中,给你车辆进出的操作序列,依次输出每辆车的车位编号。
输入格式
第一行,两个整数 n
和 m,表示停车场大小和操作数;
接下来 m
行,每行两个整数 F
和 x F 是 1表示编号为 x
的车进停车场; F
是 2表示编号为 x
的车出停车场;
保证操作合法,即:出停车场的车一定目前仍在停车场里;停车场内的车不会超过 n;
输出格式
对于所有操作 1,输出一个整数,表示该车车位的编号
样例输入
7 11
1 15
1 123123
1 3
1 5
2 123123
2 15
1 21
2 3
1 6
1 7
1 8
样例输出
1
7
4
2
7
4
1
3
提示
【数据范围】
对 30%的数据n<=1000,m<=1000对
60%的数据 n<=200000,m<=2000
对 100%的数据 n,m<=200000,
车的编号小于等于 10^6
题解:
这个题目是个线段树,其实都是基本的操作,考场上脑子一抽就写挂了。。。。
线段树中维护四个量(全都是所在区间内):l(最左边种树的坐标),r(最右边种树的坐标),mid(最近邻两棵树的最大距离),p(最近邻两棵树的中间位置)
这样每个操作一都可以看成是整个区间的询问:最左边到1是不是最大的距离(答案是1),mid是不是最大距离(答案是p),最右边到r是不是最大距离(答案是n)
这样updata也好维护:now的左就是最左,now的右就是最右,now的mid就是左边的mid,跨边界的mid,和右边的mid比较
操作二是一个添加or删除的过程,删除就是全为0,添加就把所在位置添加进去
代码:
#include <cstdio>
#include <iostream>
#include <cstring>
#define INF 1e9
#define N 200000
using namespace std;
int wz[1000006];
struct hh
{
int l,r,mid,en;
}tree[N*4];
void updata(int now)
{
if (tree[now<<1].l!=0) tree[now].l=tree[now<<1].l; else tree[now].l=tree[(now<<1)|1].l;
if (tree[now<<1|1].r!=0) tree[now].r=tree[now<<1|1].r; else tree[now].r=tree[now<<1].r;
tree[now].mid=tree[now<<1].mid;
tree[now].en=tree[now<<1].en;
if (tree[now<<1|1].l!=0 && tree[now<<1].r!=0)
{
int ss=(tree[now<<1|1].l-tree[now<<1].r)/2;
if (ss>tree[now].mid)
{
tree[now].mid=ss;
tree[now].en=(tree[now<<1|1].l+tree[now<<1].r)/2;
}
}
if (tree[now<<1|1].mid>tree[now].mid)
{
tree[now].mid=tree[now<<1|1].mid;
tree[now].en=tree[now<<1|1].en;
}
}
void work(int now,int l,int r,int mb,int id)
{
if (l==r)
{
if (id==1)
{
tree[now].l=l; tree[now].r=r;
tree[now].mid=0; tree[now].en=0;
}
else
{
tree[now].l=0; tree[now].r=0;
tree[now].mid=0; tree[now].en=0;
}
return;
}
int mid=(l+r)>>1;
if (mb<=mid) work(now<<1,l,mid,mb,id);
else work(now<<1|1,mid+1,r,mb,id);
updata(now);
}
int main()
{
freopen("park.in","r",stdin);
freopen("park.out","w",stdout);
int n,m,i,j;
scanf("%d%d",&n,&m);
for (i=1;i<=m;i++)
{
int id,x;
scanf("%d%d",&id,&x);
if (id==1)
{
if (tree[1].l==0)
{
wz[x]=1;
}
else
{
int sum=-INF;
if (tree[1].l-1>sum)
{
sum=tree[1].l-1; wz[x]=1;
}
if (tree[1].mid>sum)
{
sum=tree[1].mid; wz[x]=tree[1].en;
}
if (n-tree[1].r>sum)
{
sum=n-tree[1].r; wz[x]=n;
}
}
printf("%d\n",wz[x]);
work(1,1,n,wz[x],1);
}
else work(1,1,n,wz[x],2);
}
}
【问题描述】
平面内给 n
个点,记横坐标最小的点为A,最大的点为B,现在小Y想要知道在
每个点经过一次(A
点两次)的情况下从A走到B,再回到A
的最短路径。但他是个强
迫症患者,他有许多奇奇怪怪的要求与限制条件:
1.从A走到B
时,只能由横坐标小的点走到大的点。
2.由B
回到A
时,只能由横坐标大的点走到小的点。
3.有两个特殊点b1
和b2,b1
在 0
到n-1 的路上,b2
在n-1
到0 的路上。
请你帮他解决这个问题助他治疗吧!
【输入格式】
第一行三个整数n,b1,b2,(0<b1,b2<n-1
且b1<>b2)。n
表示点数,从0
到n-1 编
号,b1 和b2
为两个特殊点的编号。
以下n
行,每行两个整数 x、y
表示该点的坐标(0<=x,y<=2000),从0
号点顺序
给出。DoctorGao为了方便他的治疗,已经将给出的点按x
增序排好了。
【输出格式】
输仅一行,即最短路径长度(精确到小数点后面2
位)
【样例输入输出】
paths.in
5 1 3
1 3
3 4
4 1
7 5
8 3
paths.out
18.18
【样例解释】 最短路径:0->1->4->3->2->0
【数据范围】
20%的数据n<=20
60%的数据n<=300
100%的数据n<=1000
对于所有数据x,y,b1,b2如题目描述.
题解:
这是个dp,考场上怎么也没往这方面想。。。我们可以考虑为从1走到n两遍,求两条不相交的路径最小
为了不转移到f[i][i]就让k=max(i,j)+1
然后特殊点什么的特判就好了
代码:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
struct hh
{
int x,y;
}s[1005];
int n,b1,b2;
bool a[1005];
double f[1005][1005],minn=1000000000;
double ll(int a,int b)
{
return sqrt(double((s[a].x-s.x)*(s[a].x-s[b].x)+(s[a].y-s[b].y)*(s[a].y-s[b].y)));
}
int main()
{
freopen("paths.in","r",stdin);
freopen("paths.out","w",stdout);
int i,x,y,j;
scanf("%d%d%d",&n,&b1,&b2);b1++,b2++;
for (i=1;i<=n;i++)
scanf("%d%d",&s[i].x,&s[i].y);//f[i][j]表示第一个点走到i,第二个点走到j的最短距离
memset(f,0x7f,sizeof(f));
f[1][1]=0;
for (i=1;i<=n;i++)
for (j=1;j<=n;j++)
if (i!=j || i==1)
{
int k=max(i,j)+1;
if (k==n+1)
{
if (j==n) f
=min(f
,f[i][j]+ll(i,n));
if (i==n) f
=min(f
,f[i][j]+ll(j,n));
continue;
}
if (k!=b1) f[i][k]=min(f[i][k],f[i][j]+ll(j,k));
if (k!=b2) f[k][j]=min(f[k][j],f[i][j]+ll(i,k));
}
printf("%.2lf",f
);
}
[b]T3题目:
时间限制 : 20000MS空间限制 : 265536 KB
问题描述
刚拿到驾照的 KJ
总喜欢开着车到处兜风,玩完了再把车停到阿 Q的停车场里,虽然她对自己停车的水平很有信心,但她还是不放心其他人的停车水平,尤其是 Kelukin。于是,她每次都把自己的爱车停在距离其它车最远的一个车位。KJ觉得自己这样的策略非常科学,于是她开始想:在一个停车场中有一排车位,从左到右编号为
1到 n,初始时全部是空的。有若干汽车,进出停车场共 m次。对于每辆进入停车场的汽车,会选择与其它车距离最小值最大的一个车位,若有多个符合条件,选择最左边一个。KJ想着想着就睡着了,在她一旁的
Kelukin想帮她完成这个心愿,但是他又非常的懒,不愿意自己动手,于是就把这个问题就留给了你:在 KJ
理想的阿 Q
的停车场中,给你车辆进出的操作序列,依次输出每辆车的车位编号。
输入格式
第一行,两个整数 n
和 m,表示停车场大小和操作数;
接下来 m
行,每行两个整数 F
和 x F 是 1表示编号为 x
的车进停车场; F
是 2表示编号为 x
的车出停车场;
保证操作合法,即:出停车场的车一定目前仍在停车场里;停车场内的车不会超过 n;
输出格式
对于所有操作 1,输出一个整数,表示该车车位的编号
样例输入
7 11
1 15
1 123123
1 3
1 5
2 123123
2 15
1 21
2 3
1 6
1 7
1 8
样例输出
1
7
4
2
7
4
1
3
提示
【数据范围】
对 30%的数据n<=1000,m<=1000对
60%的数据 n<=200000,m<=2000
对 100%的数据 n,m<=200000,
车的编号小于等于 10^6
题解:
这个题目是个线段树,其实都是基本的操作,考场上脑子一抽就写挂了。。。。
线段树中维护四个量(全都是所在区间内):l(最左边种树的坐标),r(最右边种树的坐标),mid(最近邻两棵树的最大距离),p(最近邻两棵树的中间位置)
这样每个操作一都可以看成是整个区间的询问:最左边到1是不是最大的距离(答案是1),mid是不是最大距离(答案是p),最右边到r是不是最大距离(答案是n)
这样updata也好维护:now的左就是最左,now的右就是最右,now的mid就是左边的mid,跨边界的mid,和右边的mid比较
操作二是一个添加or删除的过程,删除就是全为0,添加就把所在位置添加进去
代码:
#include <cstdio>
#include <iostream>
#include <cstring>
#define INF 1e9
#define N 200000
using namespace std;
int wz[1000006];
struct hh
{
int l,r,mid,en;
}tree[N*4];
void updata(int now)
{
if (tree[now<<1].l!=0) tree[now].l=tree[now<<1].l; else tree[now].l=tree[(now<<1)|1].l;
if (tree[now<<1|1].r!=0) tree[now].r=tree[now<<1|1].r; else tree[now].r=tree[now<<1].r;
tree[now].mid=tree[now<<1].mid;
tree[now].en=tree[now<<1].en;
if (tree[now<<1|1].l!=0 && tree[now<<1].r!=0)
{
int ss=(tree[now<<1|1].l-tree[now<<1].r)/2;
if (ss>tree[now].mid)
{
tree[now].mid=ss;
tree[now].en=(tree[now<<1|1].l+tree[now<<1].r)/2;
}
}
if (tree[now<<1|1].mid>tree[now].mid)
{
tree[now].mid=tree[now<<1|1].mid;
tree[now].en=tree[now<<1|1].en;
}
}
void work(int now,int l,int r,int mb,int id)
{
if (l==r)
{
if (id==1)
{
tree[now].l=l; tree[now].r=r;
tree[now].mid=0; tree[now].en=0;
}
else
{
tree[now].l=0; tree[now].r=0;
tree[now].mid=0; tree[now].en=0;
}
return;
}
int mid=(l+r)>>1;
if (mb<=mid) work(now<<1,l,mid,mb,id);
else work(now<<1|1,mid+1,r,mb,id);
updata(now);
}
int main()
{
freopen("park.in","r",stdin);
freopen("park.out","w",stdout);
int n,m,i,j;
scanf("%d%d",&n,&m);
for (i=1;i<=m;i++)
{
int id,x;
scanf("%d%d",&id,&x);
if (id==1)
{
if (tree[1].l==0)
{
wz[x]=1;
}
else
{
int sum=-INF;
if (tree[1].l-1>sum)
{
sum=tree[1].l-1; wz[x]=1;
}
if (tree[1].mid>sum)
{
sum=tree[1].mid; wz[x]=tree[1].en;
}
if (n-tree[1].r>sum)
{
sum=n-tree[1].r; wz[x]=n;
}
}
printf("%d\n",wz[x]);
work(1,1,n,wz[x],1);
}
else work(1,1,n,wz[x],2);
}
}
相关文章推荐
- 【loli的胡策】NOIP训练8.10(数论+树形dp+贪心)
- 【loli的胡策】NOIP训练10.2(快速幂+树形期望dp)
- 【loli的胡策】NOIP训练7.20(二分+主席树)
- 【loli的胡策】NOIP训练8.12(二分?+dp?)
- 【loli的胡策】NOIP训练8.15(找规律+暴力)
- 【loli的胡策】训练1.7(hash+记忆化搜索+AC自动机+矩阵优化dp)
- 【loli的胡策】训练1.14(组合数学+概率期望+乱搞)
- 【充满原题的胡策】训练2.26 T1(线性基+讲解)
- 【年前的胡策】训练2.12(贪心)
- 【充满原题的胡策】训练2.26 T2(三维计算几何)
- JZOJ 4628 立方体【NOIP2016提高A组模拟7.15】
- 【XJOI-NOIP16提高模拟训练9】题解。
- 记忆化搜索(游戏NOIP17提高模拟训练11)
- NOIP训练总结
- 纪中训练 day14【NOIP普及组】模拟赛B组&C组 解题报告
- 【20141106noip训练】动态规划第二题:血缘关系
- 【NOIP2016提高A组模拟7.15】立方体
- 【NOIP2016提高A组模拟7.15】颜料大乱斗 题解+代码
- 颜料大乱斗【NOIP2016提高A组模拟7.15】
- 2017 9.17 NOIP模拟 胡策题总结