您的位置:首页 > 其它

【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

样例输出 















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);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: