1. 程式人生 > >【Atcoder - ARC102D/ABC108D】All Your Paths are Different Lengths

【Atcoder - ARC102D/ABC108D】All Your Paths are Different Lengths

@All Your Paths are Different [email protected]


@題目描述 - [email protected]

Time limit : 2sec / Memory limit : 1024MB

Score : 700 points

Problem Statement
You are given an integer L. Construct a directed graph that satisfies the conditions below. The graph may contain multiple edges between the same pair of vertices. It can be proved that such a graph always exists.

The number of vertices, N, is at most 20. The vertices are given ID numbers from 1 to N.
The number of edges, M, is at most 60. Each edge has an integer length between 0 and 10^6 (inclusive).
Every edge is directed from the vertex with the smaller ID to the vertex with the larger ID. That is, 1,2,…,N is one possible topological order of the vertices.

There are exactly L different paths from Vertex 1 to Vertex N. The lengths of these paths are all different, and they are integers between 0 and L−1.
Here, the length of a path is the sum of the lengths of the edges contained in that path, and two paths are considered different when the sets of the edges contained in those paths are different.

Constraints
2≤L≤10^6
L is an integer.

Input
Input is given from Standard Input in the following format:
L

Output
In the first line, print N and M, the number of the vertices and edges in your graph. In the i-th of the following M lines, print three integers ui,vi and wi, representing the starting vertex, the ending vertex and the length of the i-th edge. If there are multiple solutions, any of them will be accepted.

Sample Input 1
4
Sample Output 1
8 10
1 2 0
2 3 0
3 4 0
1 5 0
2 6 0
3 7 0
4 8 0
5 6 1
6 7 1
7 8 1

In the graph represented by the sample output, there are four paths from Vertex 1 to N=8:
1 → 2 → 3 → 4 → 8 with length 0
1 → 2 → 3 → 7 → 8 with length 1
1 → 2 → 6 → 7 → 8 with length 2
1 → 5 → 6 → 7 → 8 with length 3
There are other possible solutions.

Sample Input 2
5
Sample Output 2
5 7
1 2 0
2 3 1
3 4 0
4 5 0
2 4 0
1 3 3
3 5 1

@大致題意@

構造一個 n 點 m 邊的有向帶權圖,要求邊必須從節點編號小的連向結點編號大的。允許重邊。要使得從結點 1 到結點 n 恰好有 L 條路徑,這 L 條路徑的長度互不相同且在[0,L-1]範圍內。
n<=20, m<=60, L<=10^6

@分析[email protected]

【蠻有趣的構造題】
首先觀察資料範圍,發現2^20 = 1048576 ≈ 1000000 = 10^6……
你要說這是巧合我絕對不信。
然後,注意到題目特別提到了“重邊”。
然後,“用幾個數之和表示[0,L-1]的所有數”,如果單看這個沒什麼思路,但如果結合上面資料範圍的話……
二進位制表達。
兩個點之間連兩條邊,權值分別為0與2^i。
大致的構造思路就差不多是這樣,詳細細節在下面。

@分析[email protected]

我們不妨總是令n=20。
對於2<=i<=19的結點,我們i與i+1之間連兩條邊,一條權值為 0 ,一條權值為 2 20 i 1
這樣,如果1號結點向i號結點連一條權值為w的邊,那麼從1到20就會出現 w + [ 0...2 20 i 1 ] 的所有數。

接下來?我們不妨採用數位dp的思想。

假設將L轉化成二進位制形式,即令 L = i = 0 2 i b [ i ] L 。一個小於L的數x應該具有什麼特徵呢?如果存在一個p,使得:
1 i = p + 1 2 i b [ i ] L = i = p + 1 2 i b [ i ] x (前面所有數位相同)
2 b [ p ] L > b [ p ] x (當前數位L>x)
兩個條件成立,則x < L。這個時候我們發現如果(2)成立,則 b [ p ] L = 1

所有小於L的數應該是如下形式的集合並:
i = p + 1 2 i b [ i ] L + 0 + [ 0...2 p 1 ] ( b [ p ] L = 1 ) (前面所有數位相同,當前數位為0,後面的數位隨便選)

構造方法就出來了:如果 b [ p ] L = 1 ,則由1向(20-p)連一條邊權為 i = p + 1 2 i b [ i ] L 的邊。

但是我們發現如果完全按照上面的思路構造,當p=19時,我們將會1向1連邊……
所以我們必須特判 p = 19 的情況。對於這種情況,我們將1與2之間連兩條邊,邊權為 0 2 18

@程式碼@

【實現上為了簡單,我用了lowbit列舉L的每一個含有1的位】
如果還有什麼問題可以參考程式碼或者是留言給我~我會盡力解答的~

#include<cmath>
#include<cstdio>
#include<vector>
using namespace std;
struct node{
    int u, v, w;
    node(int _u, int _v, int _w):u(_u), v(_v), w(_w){}
};
vector<node>ans;
inline int lowbit(int x) {
    return x & -x;
}
int main() {
    int L;
    scanf("%d", &L);
    for(int i=19;i>1;i--) {
        ans.push_back(node(i, i+1, 0));
        ans.push_back(node(i, i+1, (1<<(20-i-1))));
    }
    while( L ) {
        int p = int(log2(lowbit(L)));
        if( p == 19 ) {
            ans.push_back(node(1, 2, 0));
            ans.push_back(node(1, 2, 1<<18));
        }
        else ans.push_back(node(1, 20-p, L^lowbit(L)));
        L ^= lowbit(L);
    }
    printf("%d %d\n", 20, ans.size());
    for(int i=0;i<ans.size();i++)
        printf("%d %d %d\n", ans[i].u, ans[i].v, ans[i].w);
} 

@[email protected]

就是這樣,新的一天裡,也請多多關照哦(ノω<。)ノ))☆.。~