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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章