【浙江省學軍中學-比賽試題】題解(補題中,未完)

A. 核酸檢測


時間限制100ms100ms
空間限制128MB128MB

題目描述

新型冠狀病毒疫情爆發!某城市內大量疑似患者被集中到各個隔離點,初期確診非常困難,專家團隊需要依次到這些隔離點去現場指導,爲疑似患者進行核酸檢測。
城市共有 n(n+1)n*(n+1) 個隔離點,它們形成了 nnn+1n+1 列的方陣。我們用 (r,c)(r,c) 表示位於從上到下第 rr 行、從左到右第 cc 列的隔離點。例如,左上角的座標是 (1,1)(1,1) ,右下角的座標是 (n,n+1)(n, n+1)
該城市的交通結構比較特殊,任意兩個對角方向相鄰隔離點之間有一條雙向通路
此外,在方陣的邊界處有一條順時針運行的單向地鐵環線。下圖是一個 n=5n=5 的城市示意圖:
A題P1
專家可以從任意一個隔離點出發,之後你可以沿着道路或乘坐地鐵前往其他隔離點。
走過一條道路、乘坐一段地鐵都需要 11 單位時間。在隔離點處進行核酸檢測所需的時間忽略不計。
專家迫切想要知道最少需要多少時間才能完成所有隔離點的核酸檢測。請求出最少需要的時間,以及一條路線。

輸入描述

輸入共一行,包含一個整數 n(2n(2 \leq nn \leq 100100)),表示隔離點方陣的行數。

輸出描述

第一行輸出一個整數,表示花費的時間 TT
接下來包含 T+1T+1 行。這部分的第 ii 行包含兩個整數 xix_i ,, yiy_i,用一個空格隔開,表示 你構造出的路線經過的第 ii 個隔離點座標是 ((xix_i ,, yiy_i))
如果存在多種可行的方案,請輸出任意一種可行的方案。
樣例輸入

2

樣例輸出

5
1 1
1 2
1 3
2 3
2 2
2 1

限制及約定
本題採用子任務形式評測。
A題P2
對於所有數據,滿足 22 \leq nn \leq 100100


題目分析:
①手玩 n=23n=2、3 的樣例,發現畫法有多種,要轉化爲輸出格式的話,就說明走法有特定格式,如果亂走可以得到正確答案的話,那麼很難用程序語言去解決這道題。也就是說這題有算法規律,簡單來說就是有固定走法。
②考慮到 nn 的規模越來越大,那麼根據以前規律題的特點,那麼畫圖題可以盲猜一個分治策略。
分治:由於斜着走,那麼每次都會引起 xxyy 座標的變化,顯然座標變化範圍爲 ±\pm11。那麼我們再盲猜我們對圖進行分開處理,每兩列形成一個圖。由於只有兩列 且 第一列可以橫向走,那麼我們只能採取螺旋走法,才能把所有點踩一遍。
nn 是奇數的時候:
A題P3
nn 是偶數的時候:
A題P4
然後代入幾個點座標觀察橫縱座標變化,AC代碼如下所示:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdlib>
using namespace std;
int read()
{
    int rt = 0, in = 1; char ch = getchar();
    while(ch < '0' || ch > '9') {if(ch == '-') in = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {rt = rt * 10 + ch - '0'; ch = getchar();}
    return rt * in;
}
int main()
{
    int n = read();
    printf("%d\n",n*(n+1)-1);
    if(n % 2 == 1)
    {
        for(int i = 1; i <= n; i += 2)
        {
            printf("1 %d\n1 %d\n",i, i+1);
            for(int j = 2; j <= n; j++)
                printf("%d %d\n",j, i + ( (j % 2 == 0) ? 1 : 2) - 1);
            for(int j = n; j >= 2; j--)
                printf("%d %d\n",j, i + ( (j % 2 == 0) ? 2 : 1) - 1);
        }
    }
    else if(n % 2 == 0)
    {
        for(int i = 1; i <= n; i += 2)
        {
            printf("1 %d\n",i);
            for(int j = 2; j <= n; j++)
                printf("%d %d\n",j, i + ( (j % 2 == 1) ? 1 : 2) - 1 );
            for(int j = n; j >= 2; j--)
                printf("%d %d\n",j, i + ( (j % 2 == 1) ? 2 : 1) - 1 );
            printf("1 %d\n",i+1);
        }
        for(int i = 1; i <= n; i++)
            printf("%d %d\n",i, n+1);
    }
    return 0;
}

B. 齊心抗疫

時間限制:300ms300ms
空間限制:512MB512MB

題目描述

某市有 nn 個縣,並有 n1n-1 條雙向高速公路連通這 nn 個縣,每條高速公路的長度爲 11
受疫情影響,第 ii 個縣裏有 aia_i 個患者。爲了讓疫情較輕的縣幫助疫情嚴重的縣,政府決定選擇兩個縣 x,yx,yxx 的疫情較爲嚴重, yy 的疫情較爲嚴重(即 axa_x \leq aya_y),並讓縣 xx 幫助縣 yy 。縣 xx 將爲縣 yy 的每一個患者送一份醫療物資,以最短路從 xxyy 運輸,運送一份醫療物資通過長度爲 的高速公路需要花費 11 元,由政府掏錢報銷。
請問如果任意選擇兩個縣實施幫扶計劃,政府最多要花多少錢?

輸入描述

第一行,一個正整數 nn
第二行, nn 個正整數,第 ii 個表示 aia_i
接下來 n1n-1 行,每行兩個數 u,vu,v,表示縣 uu 和縣 vv 之間有一條高速公路。

輸出描述

一個數表示答案。

樣例輸入

8
3 1 4 1 5 9 2 6
1 2
2 3
2 4
1 5
5 6
4 8
3 7

樣例輸出

45

樣例如圖所示:
B題P1
讓⑦援助⑥是最優解。

限制及約定

本題採用子任務形式評測。
B題P2
對於所有數據,滿足 22 \leq nn \leq 500005000011 \leq aia_i \leq 10001000


題目分析:
①簡化題意:求 max(max(a[i],a[j])dis[i][j])max( max(a[i],a[j]) * dis[i][j])。( i,ji,j 均是屬於[1,n][1,n]的點,dis[i][j]dis[i][j]表示 i,ji,j 之間的距離。)
②最暴力做法是求出任意兩點的距離,然後取兩點較大價值暴力計算。時間複雜度:O(nlogN)O(n*logN)。考慮優化,只能用 線段樹分治 等數據結構來維護。但看到排行榜大佬十分不到就A了此題,那應該是利用了某些樹的性質。
③枚舉點 uu ,難點在於尋找距離其最遠的點 vv。由樣例啓發感覺點 vv 似乎在樹的直徑上,可以大概猜測任意一點 uu,其最遠點 vv 一定是樹的直徑兩端點中的一個。證明如下:
part1part1:尋找樹的直徑時,我們是以任意點 (設爲 ii) 出發,找到距離該點最遠的點(設爲 uu),然後以最遠的點(uu)爲起點,尋找距離該點(uu)最遠的點(vv).那麼路徑 u>vu->v 就是樹的直徑。
part2part2:有上述可得,無論我們以哪一個點爲起點,我們最後得到的樹的直徑是固定不變的(樹的直徑可能有多條,但並不影響該算法正確性)。而距離我們選擇點(ii)一定是點(uu)或者點(vv) (因爲這是由找樹的直徑的算法決定的)。
因此可得對於任意點 ii ,其最遠點是 樹的直徑 兩端點中的一個。
④那麼我們預處理上述流程,先進行 dfs1dfs1 確定樹的直徑的一端,然後 dfs2dfs2 找樹的另一端,並處理好該端距離其他所有點的距離(dis1[i]dis1[i])。然後再以另一端爲起點,再跑一遍 dfs3dfs3,處理另一端點距離其他所有點的距離(dis2[i]dis2[i])。最後一遍for循環統計答案,此題AC。

AC代碼如下所示:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdlib>
using namespace std;
int n,rt1,rt2,Max,ans;
int head[505050],f[50050],dis1[50050],dis2[50050];
struct list
{
    int to,nxt;
}e[101010];
int read()
{
    int rt = 0, in = 1; char ch = getchar();
    while(ch < '0' || ch > '9') {if(ch == '-') in = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {rt = rt * 10 + ch - '0'; ch = getchar();}
    return rt * in;
}
void add_edge(int u, int v)
{
    e[++head[0]].to = v;
    e[head[0]].nxt = head[u];
    head[u] = head[0];
}
void dfs1(int x, int depth, int last)
{
    if(depth > Max) Max = depth, rt1 = x;
    for(int i = head[x]; i; i = e[i].nxt)
        if(e[i].to != last)
            dfs1(e[i].to, depth+1, x);
}
void dfs2(int x, int depth, int last)
{
    if(depth > Max) Max = depth, rt2 = x;
    for(int i = head[x]; i; i = e[i].nxt)
        if(e[i].to != last)
        {
            dis1[e[i].to] = dis1[x] + 1;
            dfs2(e[i].to, depth+1, x);
        }
           
}
void dfs3(int x, int last)
{
    for(int i = head[x]; i; i = e[i].nxt)
        if(e[i].to != last)
        {
            dis2[e[i].to] = dis2[x] + 1;
            dfs3(e[i].to,  x);
        }
}
int main()
{
    n = read();
    for(int i = 1; i <= n; i++) f[i] = read();
    for(int i = 1; i < n; i++)
    {
        int u = read(), v = read();
        add_edge(u, v); add_edge(v, u);
    }
    rt1 = 1, Max = 1;
    dfs1(1, 1, 0);
    rt2 = 1, Max = 1;
    dfs2(rt1, 1, 0);
    dfs3(rt2, 0);
    for(int i = 1; i <= n; i++)
        ans = max(ans, max( max(f[i],f[rt1]) * dis1[i], max(f[i],f[rt2]) * dis2[i]) );
    printf("%d",ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章