1. 程式人生 > >##Codeforces Round #542 題解

##Codeforces Round #542 題解

cond put 一個人 另一個 題意 lse 算法 har while

Codeforces Round #542

abstract
I決策中的獨立性,
II聯通塊染色板子
IIIVoronoi diagram O(N^2 logN)
VI環上距離分類討論加取模,最值中的決定性元素
V代數構造

B

題意

2n個數排成一行,1..n各出現兩次,兩個人分別從1按順序走到n(每個數只能被一個人路過),問他們兩個人的距離和的最小值(兩個數之間的距離等於他們位置(下標)值差)?


題解

將每個數的兩個位置用pair存起來(eg : pos[1].x , pos[1].y)。
首先我們發現兩個人的選擇其實並不獨立,如果一個人選擇了一條路,另一個人的路也就確定了。其次我們發現每兩個數之間的路徑是相互獨立的。

所以對於每一段 i to i+1 我們只要取min(abs(pos[i + 1].x - pos[i].x) + abs(pos[i + 1].y - pos[i].y), abs(pos[i + 1].x - pos[i].y) + abs(pos[i + 1].y - pos[i].x))即可。 最後求和


代碼

//頭文件省略
cin >> n;
    rep(i, 1, 2 * n) {
        int a;
        cin >> a;
        if (pos[a].x == 0)pos[a].x = i;
        else pos[a].y = i;
    }
    ll ans = pos[1].x + pos[1].y - 2;;

    rep(i, 1, n - 1)c[i] = min(abs(pos[i + 1].x - pos[i].x) + abs(pos[i + 1].y - pos[i].y), abs(pos[i + 1].x - pos[i].y) + abs(pos[i + 1].y - pos[i].x));
    rep(i, 1, n-1)ans += c[i];
    
    cout << ans;

C

題意

求網格圖上聯通塊之間的最短距離平方。

sample input
5
1 1
5 5
00001
11111
00111
00110
00110

sample input
10

其中0代表陸地,1代表海洋


題解

直接跑一遍聯通塊染色,然後暴力O(n^4)就能過。
優化的方法是取邊界,O(n^3)。
最優解是Voronoi diagram O(N^2 logN)


代碼

#include<algorithm>
#include<iostream>
#include<sstream>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
#include<math.h>
#include<stdio.h>
#include<vector>
#include<queue>
#include<string>
#include<ctime>
#include<stack>
#include<map>
#include<set>
#include<list>
using namespace std;
#define rep(i,j,k) for(int i = (int)j;i <= (int)k;i ++)
#define REP(i,j,k) for(int i = (int)j;i < (int)k;i ++)
#define per(i,j,k) for(int i = (int)j;i >= (int)k;i --)
#define debug(x) cerr<<#x<<" = "<<(x)<<endl
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define MD(x) x%=mod
#define FAST_IO ios_base::sync_with_stdio(false); cin.tie(nullptr)
#define precise(x) fixed << setprecision(x)


//#define x first
//#define y second
typedef double db;
typedef long long ll;
const int MAXN = 256;;
const int maxn = 2e5+2;
const int INF = 1e9;
const db eps = 1e-7;
const int mod = 1e9 + 7;


//a[maxn], b[maxn];
int A[maxn];
char mmp[55][55];
int color[55][55];
int n,m;
int dir[4][2] = { 0,1 ,1,0, 0,-1, -1,0 };
int colornum = 0;

void dfs(int x, int y) {
    if (color[x][y])return;
    color[x][y] = colornum;
    rep(i, 0, 3) {
        int dx = x + dir[i][0];
        int dy = y + dir[i][1];
        if (dx<1 || dx>n || dy<1 || dy>n)continue;
        if(mmp[dx][dy]=='0')dfs(dx, dy);
    }
}
void bfs(int x, int y) {
    queue<pair<int, int> >Q;
    Q.push({ x,y });
    color[x][y] = colornum;
    while (!Q.empty()) {
        auto  v = Q.front();
        Q.pop();
        rep(i, 0, 3) {
            int dx = v.first + dir[i][0];
            int dy = v.second + dir[i][1];
            if (color[dx][dy])continue;
            if (dx<1 || dx>n || dy<1 || dy>n)continue;
            
            if (mmp[dx][dy] == '0')Q.push({ dx,dy }),color[dx][dy]=colornum;
        }

    }
}

int main() {
    FAST_IO;
    cin >> n;
    int r1, c1, r2, c2;
    cin >> r1 >> c1 >> r2 >> c2;
    rep(i, 1, n)cin >> mmp[i] + 1;
    
    
    rep(i, 1, n)rep(j, 1, n) if(mmp[i][j]=='0'){
        if (!color[i][j]) {
            colornum++;
            bfs(i, j);  
        }
    }

    int ans = 1e9;
    if (color[r1][c1] == color[r2][c2])ans = 0;
    else {
        rep(i, 1, n)rep(j, 1, n)if (color[i][j] == color[r1][c1]) {
            rep(ii, 1, n)rep(jj, 1, n)if (color[ii][jj] == color[r2][c2]) {
                ans = min(ans, (i - ii)*(i - ii) + (j - jj)*(j - jj));
            }
        }
    
    }
    cout << ans << endl;
    cin >> n;
    }

D

題意

有一個n個結點的環,每個結點上有一些糖果,每個糖果都有一個想要到達的結點,你現在從某個結點出發順時針走,每次經過一個結點只能帶一個糖果,問從某點出發最少走多少路程可以把所有糖果運送到它們想要到達的結點? 出發點取遍1到n。


題解

我們發現從某結點出發拿一個糖果,最壞情況下走一圈以後一定可以把它放好。而如果某節點有兩顆糖果,那麽至少要超過一圈才能放好。
所以 如果記所有結點中糖果數量最大值為mx,最多mx+1圈必定能全部送完。
而對一個結點來說,只有它的最後一個糖果想到達的結點決定了運完該點所有糖果總路程的長短,因此我們貪心地把路程最短的糖果留到最後拿即可。
於是問題就變成了環上求路程了,要用到取模,和分類討論。(分類很不擅長,討論了半天orz坑點:candy[now].size()==mx-1&&mx>1如果不加後面這個mx>1就會討論根本沒有糖果的情況,導致wa)


代碼

//頭文件省略
int n;
int a[maxn], b[maxn];
vector<int> candy[maxn];
int mindis[maxn];
int lastmin[maxn];
int main() {
    FAST_IO;
    int n, m;
    cin >> n >> m;
    rep(i, 1, n)mindis[i] = 1e9,lastmin[i]=0;
    rep(i, 1, m) {
        cin >> a[i] >> b[i];
        candy[a[i]].push_back(b[i]);
        mindis[a[i]] = min(mindis[a[i]], (b[i] - a[i]+n)%n);

    }
    
    int mx = 0;
    rep(i, 1, n)mx = max(mx, (int)candy[i].size());
    rep(i, 1, n) {
        rep(j, 1, n) {
            int now = (i - j+n)%n; if (now == 0)now = n;
            if(candy[now].size()==mx)
                lastmin[i] = max(lastmin[i], 
                (now - i + n) % n+mindis[now]);
            else if(candy[now].size()==mx-1&&mx>1)
                lastmin[i] = max(lastmin[i],
                (now - i + n) % n + mindis[now]-n);
        }   
    }

    //rep(i, 1, n)cout << mindis[i] << ' '; cout << endl;
    //rep(i, 1, n)cout << lastmin[i] << ' '; cout << endl;
    
    rep(i, 1, n) {
    
        cout <<n*(mx-1) + lastmin [i] << ' ';
    }
     
    cin >> n;
    }

E

題意

構造題,給你一個問題,和一個錯誤算法,讓你構造數據使得正確答案與錯誤代碼答案相差k。

問題是有一個數列,a[i]<1e6,n<2e3;定義 f(l,r)=(sum[r]-sum[l-1])*(r-l+1),sum為前綴和,求max{f(l,r)} for l=1 to n, r=l to n;

錯誤代碼是線性滑窗掃一遍,每次維護一個區間窗口,如果小於0就舍棄原區間,重新開一個新區間。如果和大於0就不停加數。


題解

代碼很簡單,構造很巧妙,證明很數學
考慮形如 -1 , x, x, x,...x,y的數列,共n個x,1個y都為正數(其中x都是1e6,為了滿足原數列每個數<=1e6的條件),錯誤代碼給出的解是(x*n+y)*(n+1);而正確答案應是(x*n+y-1)*(n+2)兩者差為n*x+y-2 令x=1e6,n=k/x,y=k%x即可。(代數不好推了半天orz)


代碼

//頭文件省略
        int k; cin >> k;
     n = 1;
     int S = -1;
     int last = -1;
     while (S-n+1<k) {
         n++;
         int delta = k-(S - n + 1) ;
         if (delta <= 1e6) { last = delta; break; }
         else S += 1e6;
     }
     cout << n << endl;
     cout << -1 << ' ';
     rep(i,1,n-2)cout<< 1000000 << ' ';
     if (last != -1)cout << last << endl;
    

##Codeforces Round #542 題解