NOIP2013复赛提高组3题

vijos1842,1843,1846

华容道代码题调代码时间是写代码时间不知道多少倍

题解时间

vijos1842

vijos地址:https://vijos.org/p/1842

描述

涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度。现在将每盒中的火柴各自排成一列,同一列火柴的高度互不相同,两列火柴之间的距离定义为:∑i=1n(ai−bi)2,其中 ai 表示第一列火柴中第 i 个火柴的高度,bi

表示第二列火柴中第 i 个火柴的高度。

每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小。请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最小交换次数对 99,999,997 取模的结果。

输入格式

共三行,第一行包含一个整数 n,表示每盒中火柴的数目。

第二行有 n 个整数,每两个整数之间用一个空格隔开,表示第一列火柴的高度。

第三行有 n 个整数,每两个整数之间用一个空格隔开,表示第二列火柴的高度。

输出格式

输出共一行,包含一个整数,表示最少交换次数对 99,999,997 取模的结果。

输入样例1

4
2 3 1 4
3 2 1 4

输入样例1

1

输入样例2

4
1 3 4 2
1 7 2 4

输出样例

2

限制

每个测试点1s

样例1说明

最小距离是 0,最少需要交换 1 次,比如:交换第 1 列的前 2 根火柴或者交换第 2 列的前 2 根火柴。

样例2说明

最小距离是 10,最少需要交换 2 次,比如:交换第 1 列的中间 2 根火柴的位置,再交换第 2 列中后 2 根火柴的位置。

数据范围

对于 10%的数据, 1 ≤ n ≤ 10;
对于 30%的数据,1 ≤ n ≤ 100;
对于 60%的数据,1 ≤ n ≤ 1,000;
对于 100%的数据,1 ≤ n ≤ 100,000,0 ≤火柴高度≤ 2^31 − 1。

来源

NOIP 2013 提高组 Day 1

做法

举个栗子

第一列:1 3 4 5

第二列:1 2 3 4

看起来有两种方案比较滋磁,第一种,就按照原来顺序不动(即相对大小一样的放在一个位置),现在算出来的结果是

(1-1)^2+(3-2)^2+(4-3)^2+(5-4)^2=3

第二种方案,把3和3放一起,4和4放一起,那么2只能和5一起,算出来结果是

(1-1)^2+(3-3)^2+(4-4)^2+(5-2)^2=9>3

由此,可以看出,对于a1,b1和a2,b2来说,如果a1

#include <bits/stdc++.h>
using namespace std;
const int M=100010,P=99999997;
struct huochai{int x,v;}a[M],b[M];
int n,m,i,j,ans;int t[M],tp[M];
inline bool cmp(huochai aa,huochai bb){return aa.x<bb.x;}
inline void merge(int s,int m,int e){
    int i=s,j=m+1,k=s;
    for(;i<=m&&j<=e;)
        if(t[i]>=t[j]) tp[k++]=t[j++],(ans+=m-i+1)%=P;
        else tp[k++]=t[i++];
    for(;i<=m;) tp[k++]=t[i++];
    for(;j<=e;) tp[k++]=t[j++];
    for(i=s;i<=e;i++) t[i]=tp[i];
}
inline void work(int s,int e){
    int m=s+e>>1;
    if(s<e) work(s,m),work(m+1,e),merge(s,m,e);
}
int main(){
    for(scanf("%d",&n),i=1;i<=n;i++)
        scanf("%d",&a[i].x),a[i].v=i;
    for(i=1;i<=n;i++)
        scanf("%d",&b[i].x),b[i].v=i;
    for(sort(a+1,a+n+1,cmp),sort(b+1,b+n+1,cmp),i=1;i<=n;i++)
        t[a[i].v]=b[i].v;
    work(1,n);return printf("%d\n",ans),0;
}

vijos1843

vijos地址:https://vijos.org/p/1843

描述

A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 q 辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。

输入格式

第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道路。

接下来 m 行每行 3 个整数 x、y、z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路。注意:x 不等于 y,两座城市之间可能有多条道路。

接下来一行有一个整数 q,表示有 q 辆货车需要运货。

接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意:x 不等于 y。

输出格式

输出共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出-1。

输入样例

4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3

输出样例

3
-1
3

限制

每个测试点1s

数据范围

对于 30%的数据,0 < n < 1,000,0 < m < 10,000,0 < q < 1,000;
对于 60%的数据,0 < n < 1,000,0 < m < 50,000,0 < q < 1,000;
对于 100%的数据,0 < n < 10,000,0 < m < 50,000,0 < q < 30,000,0 ≤ z ≤ 100,000。

来源

NOIP 2013 提高组 Day 1

做法

求最大生成森林,询问森林中两点路径上最小边权,用并查集判连通性,然后树上倍增解决一棵树的询问

然而我并想不到,只能是在最大生成森林里跑spfa。

然后发现倍增用到好多啊。

代码如下

#include <bits/stdc++.h>
using namespace std;
const int M=10010,inf=0x3f3f3f3f;
int n,m,q,cnt,tot,i,j,x,y,p,t;
int dep[M],he[M],f[M],fa[M][17],d[M][17],v[M];
struct edge{int x,y,v;}a[M<<3];
struct e{int ne,to,v;}e[M<<1];
inline void ins(int u,int v,int z){
    e[++tot].to=v,e[tot].ne=he[u],he[u]=tot,e[tot].v=z;
}
inline int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
inline bool cmp(edge a,edge b){return a.v>b.v;}
inline void dfs(int x){
    v[x]=1;for(int i=1;i<=16;i++){
        if(dep[x]<(1<<i)) break;
        fa[x][i]=fa[fa[x][i-1]][i-1];
        d[x][i]=min(d[x][i-1],d[fa[x][i-1]][i-1]);
    }for(int y,i=he[x];i;i=e[i].ne){
        if(v[e[i].to]) continue;
        fa[(y=e[i].to)][0]=x;
        d[y][0]=e[i].v;dep[y]=dep[x]+1;dfs(y);
    }
}
inline int lca(int x,int y){
    if(dep[x]<dep[y]) swap(x,y);
    int t=dep[x]-dep[y],i;
    for(i=0;i<=16;i++)
        if((1<<i)&t) x=fa[x][i];
    for(i=16;i>=0;i--)
        if(fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];
    if(x==y) return x;
    return fa[x][0];
}
inline int qu(int x,int y){
    int mn=inf,t=dep[x]-dep[y];
    for(int i=0;i<=16;i++)
        if((1<<i)&t)
            mn=min(mn,d[x][i]),x=fa[x][i];
    return mn;
}
int main(){
    for(memset(d,127/3,sizeof(d)),scanf("%d%d",&n,&m),i=1;i<=n;i++) f[i]=i;
    for(i=1;i<=m;i++)
        scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].v);
    for(sort(a+1,a+m+1,cmp),i=1;i<=m;i++){
        x=a[i].x,y=a[i].y,p=find(a[i].x),q=find(a[i].y);
        if(p!=q){
            f[p]=q;ins(x,y,a[i].v);ins(y,x,a[i].v);
            if((++cnt)==n-1) break;
        }
    }for(i=1;i<=n;i++) if(!v[i]) dfs(i);
    for(scanf("%d",&q),i=1;i<=q;i++){
        scanf("%d%d",&x,&y);
        if(find(x)!=find(y)){puts("-1");continue;}
        else t=lca(x,y),printf("%d\n",min(qu(x,t),qu(y,t)));
    }return 0;
}

vijos1846

vijos地址:https://vijos.org/p/1846

描述

小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次。于是,他想到用编程来完成华容道:给定一种局面,华容道是否根本就无法完成,如果能完成,最少需要多少时间。

小 B 玩的华容道与经典的华容道游戏略有不同,游戏规则是这样的:

1.在一个 n*m 棋盘上有 n*m 个格子,其中有且只有一个格子是空白的,其余 n*m-1个格子上每个格子上有一个棋子,每个棋子的大小都是 1*1 的;

2.有些棋子是固定的,有些棋子则是可以移动的;

3.任何与空白的格子相邻(有公共的边)的格子上的棋子都可以移动到空白格子上。游戏的目的是把某个指定位置可以活动的棋子移动到目标位置。

给定一个棋盘,游戏可以玩 q 次,当然,每次棋盘上固定的格子是不会变的,但是棋盘上空白的格子的初始位置、指定的可移动的棋子的初始位置和目标位置却可能不同。第 i 次玩的时候,空白的格子在第 EXi行第 EYi 列,指定的可移动棋子的初始位置为第 SXi 行第 SYi 列,目标位置为第 TXi 行第 TYi列。

假设小 B 每秒钟能进行一次移动棋子的操作,而其他操作的时间都可以忽略不计。请你告诉小 B 每一次游戏所需要的最少时间,或者告诉他不可能完成游戏。

输入格式

第一行有 3 个整数,每两个整数之间用一个空格隔开,依次表示 n、m 和 q;

接下来的 n 行描述一个 n*m 的棋盘,每行有 m 个整数,每两个整数之间用一个空格隔开,每个整数描述棋盘上一个格子的状态,0 表示该格子上的棋子是固定的,1 表示该格子上的棋子可以移动或者该格子是空白的。

接下来的 q 行,每行包含 6 个整数依次是 EXi、EYi、SXi、SYi、TXi、TYi,每两个整数之间用一个空格隔开,表示每次游戏空白格子的位置,指定棋子的初始位置和目标位置。

输出格式

输出有 q 行,每行包含 1 个整数,表示每次游戏所需要的最少时间,如果某次游戏无法完成目标则输出−1。

输入样例

3 4 2
0 1 1 1
0 1 1 0
0 1 0 0
3 2 1 2 2 2
1 2 2 2 3 2

输出样例

2
-1

限制

每个测试点1s

样例说明

棋盘上划叉的格子是固定的,红色格子是目标位置,圆圈表示棋子,其中绿色圆圈表示目标棋子。

1.第一次游戏,空白格子的初始位置是 (3, 2)(图中空白所示),游戏的目标是将初始位置在(1, 2)上的棋子(图中绿色圆圈所代表的棋子)移动到目标位置(2, 2)(图中红色的格子)上。

移动过程如下:

![这里写图片描述](https://img-blog.csdn.net/20161106110351405)

2第二次游戏,空白格子的初始位置是(1, 2)(图中空白所示),游戏的目标是将初始位置在(2, 2)上的棋子(图中绿色圆圈所示)移动到目标位置 (3, 2)上。 

![这里写图片描述](https://img-blog.csdn.net/20161106110424231)

要将指定块移入目标位置,必须先将空白块移入目标位置,空白块要移动到目标位置,必然是从位置(2,2)上与当前图中目标位置上的棋子交换位置,之后能与空白块交换位置的只有当前图中目标位置上的那个棋子,因此目标棋子永远无法走到它的目标位置,游戏无法完成。 

数据范围

对于 30%的数据,1 ≤ n, m ≤ 10,q = 1;
对于 60%的数据,1 ≤ n, m ≤ 30,q ≤ 10;
对于 100%的数据,1 ≤ n, m ≤ 30,q ≤ 500。

来源

NOIP 2013 提高组 day 2

做法

暴力bfs60分性价比不是一般地高

60分代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int M=40,N=1000010;
int n,m,i,j,T,ax,ay,x,y,bx,by,h,t;
int a[M][M],f[M][M][M][M];
int xx[4]={0,0,1,-1},yy[4]={1,-1,0,0};
struct node{int x,y,xx,yy,st;}q[N];
inline int ok(int x,int y,int xx,int yy){
    if(x<1||y<1||x>n||y>m||!a[x][y]||!a[xx][yy]) return 0;
    if(f[x][y][xx][yy]) return 0;
    f[x][y][xx][yy]=1;return 1;
}
inline void bfs(){
    memset(f,0,sizeof(f));
    scanf("%d%d%d%d%d%d",&q[0].xx,&q[0].yy,&q[0].x,&q[0].y,&ax,&ay);
    if(ax==q[0].x&&ay==q[0].y){puts("0");return;}
    for(f[q[0].x][q[0].y][q[0].xx][q[0].yy]=1,h=t=0;h<=t;){
        for(int i=0;i<4;i++){
            x=q[h].x,y=q[h].y;
            bx=q[h].xx+xx[i],by=q[h].yy+yy[i];
            if(x==bx&&y==by){x=q[h].xx;y=q[h].yy;}
            if(ok(x,y,bx,by)){
                t++;q[t].x=x;q[t].y=y;
                q[t].xx=bx;q[t].yy=by;
                q[t].st=q[h].st+1;
                if(x==ax&&y==ay){printf("%d\n",q[t].st);return;}
            }
        }h++;
    }printf("-1\n");return;
}
int main(){
    for(scanf("%d%d%d",&n,&m,&T),i=1;i<=n;i++)
       for(j=1;j<=m;j++) scanf("%d",&a[i][j]);
    for(i=1;i<=T;i++) bfs();
    return 0;
}

vijos上给了70分,好给面子啊。

这里写图片描述

满分算法要先预处理整张图(反正就30*30小的要死),代码又长又臭。

满分代码

#include <bits/stdc++.h>
using namespace std;
const int M=10010,inf=0x3f3f3f3f;
int n,m,q,cnt,tot,i,j,x,y,p,t;
int dep[M],he[M],f[M],fa[M][17],d[M][17],v[M];
struct edge{int x,y,v;}a[M<<3];
struct e{int ne,to,v;}e[M<<1];
inline void ins(int u,int v,int z){
    e[++tot].to=v,e[tot].ne=he[u],he[u]=tot,e[tot].v=z;
}
inline int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
inline bool cmp(edge a,edge b){return a.v>b.v;}
inline void dfs(int x){
    v[x]=1;for(int i=1;i<=16;i++){
        if(dep[x]<(1<<i)) break;
        fa[x][i]=fa[fa[x][i-1]][i-1];
        d[x][i]=min(d[x][i-1],d[fa[x][i-1]][i-1]);
    }for(int y,i=he[x];i;i=e[i].ne){
        if(v[e[i].to]) continue;
        fa[(y=e[i].to)][0]=x;
        d[y][0]=e[i].v;dep[y]=dep[x]+1;dfs(y);
    }
}
inline int lca(int x,int y){
    if(dep[x]<dep[y]) swap(x,y);
    int t=dep[x]-dep[y],i;
    for(i=0;i<=16;i++)
        if((1<<i)&t) x=fa[x][i];
    for(i=16;i>=0;i--)
        if(fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];
    if(x==y) return x;
    return fa[x][0];
}
inline int qu(int x,int y){
    int mn=inf,t=dep[x]-dep[y];
    for(int i=0;i<=16;i++)
        if((1<<i)&t)
            mn=min(mn,d[x][i]),x=fa[x][i];
    return mn;
}
int main(){
    for(memset(d,127/3,sizeof(d)),scanf("%d%d",&n,&m),i=1;i<=n;i++) f[i]=i;
    for(i=1;i<=m;i++)
        scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].v);
    for(sort(a+1,a+m+1,cmp),i=1;i<=m;i++){
        x=a[i].x,y=a[i].y,p=find(a[i].x),q=find(a[i].y);
        if(p!=q){
            f[p]=q;ins(x,y,a[i].v);ins(y,x,a[i].v);
            if((++cnt)==n-1) break;
        }
    }for(i=1;i<=n;i++) if(!v[i]) dfs(i);
    for(scanf("%d",&q),i=1;i<=q;i++){
        scanf("%d%d",&x,&y);
        if(find(x)!=find(y)){puts("-1");continue;}
        else t=lca(x,y),printf("%d\n",min(qu(x,t),qu(y,t)));
    }return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值