1. 程式人生 > 其它 >Codeforces Round 693 (Div 3)

Codeforces Round 693 (Div 3)

技術標籤:Codeforces線性優化

Codeforces Round 693(Div 3)

F New Year’s Puzzle

這裡就不放原題目描述了。有 2 × n 2\times n 2×n的方格,我們有 1 × 2 1\times2 1×2 2 × 1 2\times1 2×1的瓷磚,方格里有若干個點已經貼了瓷磚,問能否完美貼住?(瓷磚數量沒有限制)

輸入:
樣例數 t ( 1 ≤ t ≤ 1 0 4 ) t(1\leq t\leq 10^4) t(1t104)
整數n和m,n是方格的長(寬永遠是2),m是已經有的瓷磚數量。 1 ≤ n ≤ 1 0 9 , 1 ≤ m ≤ 2 × 1 0 5 1\leq n \leq 10^9, 1\leq m\leq 2 \times 10^5

1n109,1m2×105.
m行,每行都是 r i , c i r_i,c_i ri,ci,已經貼了的座標。 1 ≤ r i ≤ 2 , 1 ≤ c i ≤ n 1\leq r_i\leq 2, 1\leq c_i\leq n 1ri2,1cin.

Example:
Input:

3

5 2
2 2
1 4

3 2
2 1
2 3

6 4
2 1
2 3
2 4
2 6

Output:

YES
NO
NO

比如第一個例子如圖所示:
在這裡插入圖片描述
當時Div3做完E題剩下F和G,看到G人少就去做G了,最後也沒能調過。但F讓我做我估計也過不了。其實大體思路很容易想,知道肯定要貪心。從最左邊開始,比如上面的例子,第一列是空的,那就放一個豎的。第二列上面有一個,所以下面的就只能放一個橫的瓷磚。這個操作影響到第三列,那此時第三列也只能放一個橫的,直到一直操作到最後一列。那麼什麼時候得到結果呢?操作過程中,如果發現一個橫的瓷磚不得不放的地方,已經有瓷磚了,或者放了就出界了,那就只能宣佈no。如果放到最後都沒衝突,那就是yes。

這麼寫的話,要注意到比如只有第一列第一行有一個瓷磚,那麼每一次都放一個橫的瓷磚一直迴圈下去,會重複 1 0 9 10^9 109級,顯然會超時。注意到m的範圍是明顯的 n log ⁡ n n\log n nlogn級別不超時的,所以可以從m入手。

對於所有全空的列,我們就直接不考慮,因為一定不會衝突。用map記錄所有出現過瓷磚的列。關鍵是從map中上一列到下一個出現瓷磚的列,中間隔著若干空列,怎麼判斷是否衝突?不難發現,比如在(k,1)不得不放了一個橫瓷磚,那麼接下來就得在(k+1,2)放一個,接下來是(k+2,1),是一個2次的迴圈。開始我也一直沒想通程式碼裡為什麼color要加上x,其實就是用來處理這一點的。lastColor是結合了橫座標和瓷磚位置的結果。

  • 用狀壓表示一列的狀態,可以是0-3
  • hasLast表明有沒有之前某個橫瓷磚延伸過來影響到這一列
  • 要注意要加一個m[2e9],代表右邊界。很容易忘記。
//
//  main.cpp
//
//
//  Created by ji luyang on 2020/12/22.
//

#include <stdio.h>
#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <time.h>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
using namespace std;
int t;
int main() {
    cin >> t;
    while (t--) {
        int n, m;
        cin >> n >> m;
        map<int, int> mp;
        for (int i = 1; i <= m; i++) {
            int r, c;
            scanf("%d %d", &r, &c);
            mp[c] |= (1 << (r - 1));
        }
        mp[2e9] = 3;
        int flag = 1;
        int hasLast = 0, lastColor = 0;
        for (auto pi: mp) {
            int x = pi.first, mask = pi.second;
            if (mask == 3) {
                if (hasLast) {
                    flag = 0;
                    break;
                }
            } else {
                if (hasLast) {
                    int color = (x + mask) % 2;
                    if (color == lastColor) {
                        flag = 0;
                        break;
                    } else {
                        hasLast = 0;
                    }
                } else {
                    hasLast = 1;
                    lastColor = (x + mask) % 2;
                }
            }
        }
        printf("%s", flag ? "YES\n" : "NO\n");
    }
    
    return 0;
}
 

G Moving to the Capital

有向圖,n個頂點,m條邊,1是首都。m和n都是 1 0 5 10^5 105量級。設1到達城市i的經過的最少邊數是 d i d_i di。現在要城市 i ( 1 ≤ i ≤ n ) i(1\leq i \leq n) i(1in)出發,沿著邊走。我們只有一次機會從i走到j,其中 d i ≥ d j d_i \geq d_j didj,其餘都只能 d i < d j d_i < d_j di<dj這麼走。返回n個城市,按這個規則能達到的最小的 d d d
輸入第一行是樣例數t,每個樣例裡是n,m,然後是m條有向邊。
樣例輸入:

3

6 7
1 2
1 3
2 5
2 4
5 1
3 6
6 2

2 2
1 2
2 1

6 8
1 2
1 5
2 6
6 1
2 3
3 4
4 2
5 4

樣例輸出:

0 0 1 2 0 1 
0 0 
0 0 2 1 1 0 

思路也不難,想的也差不多。肯定先bfs一遍預處理出所有的 d i d_i di。既然只有一次返回走的機會,那麼我們就是要讓這次往回走返回到的d最小,因為接下來d只可能越來越大。換言之,對於城市i,我們要返回所有d比i更大且從i不斷增大d可達的點中,能走到的d最小的邊。

可以預處理出一個tomin[i]陣列,表示每個頂點直接能走到的d最小值。然後因為 n n n的大小,肯定不能從每個頂點都遍歷一遍,肯定是要邊遍歷邊更新,按某種順序進行遍歷。比賽的時候寫了增加反向邊,然後從所有d最大的點,往首都走,邊走邊更新到達的點的tomin。這是不對的,比如有的點d不是最大的,卻並不能再繼續往d增大的方向走了。其實根本不需要增加反向邊,直接利用之前bfs得到的拓撲序列入手,從後往前遍歷,遍歷到城市i,對於所有連著的城市v,如果d[v]>d[i],更新tomin[i]。此時tomin[v]也已經更新過。

//
//  main.cpp
//
//
//  Created by ji luyang on 2020/12/22.
//

#include <stdio.h>
#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <time.h>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
int t;
vector<int> g[200010];
vector<int> topo;
int d[200010], tomin[200010];
int main(int argc, char* argv[]) {
    cin >> t;
    while (t--) {
        int n, m;
        cin >> n >> m;
        memset(g, 0, sizeof(g));
        topo.clear();
        for (int i = 1; i <= m; i++) {
            int u, v;
            scanf("%d %d", &u, &v);
            g[u].push_back(v);
        }
        for (int i = 1; i <= n; i++) d[i] = 1e9;
        d[1] = 0;
        queue<int> q;
        q.push(1);
        while (!q.empty()) {
            int tmp = q.front();
            topo.push_back(tmp);
            q.pop();
            for (auto v: g[tmp]) {
                if (d[v] == 1e9) {
                    d[v] = d[tmp] + 1;
                    q.push(v);
                }
            }
        }
        for (int i = 1; i <= n; i++) tomin[i] = d[i];

        for (int i = 1; i <= n; i++) {
            for (int v: g[i]) {
                if (d[v] <= d[i]) {
                    tomin[i] = min(tomin[i], d[v]);
                }
            }
        }
        
        for (int i = n - 1; i >= 0; i--) {
            int u = topo[i];
            for (int v: g[u]) {
                if (d[v] > d[u]) {
                    tomin[u] = min(tomin[u], tomin[v]);
                }
            }
        }
        for (int i = 1; i <= n; i++) {
            printf("%d ", tomin[i]);
        }
        printf("\n");
        
    }
    
    return 0;
}