Danh sách liên kết

1. Giới thiệu

Cấu trúc danh sách liên kết

Danh sách liên kết (linked list) được dùng để lưu giữ các phân tử có cùng kiểu giá trị. Bên dưới là ví dụ minh họa sử dụng danh sách liên kết gồm 4 phần tử để lưu giữ dãy A, B, C, D. Phần tử đầu tiên của danh sách liên kết gọi là “head”.
Trong khi các phần tử trong mảng được lưu giữ liên tiếp trong bộ nhớ, thì các phần tử trong danh sách liên kết có thể nằm ở các vị trí khác nhau trong bộ nhớ. Mỗi phần tử (node) trong danh sách liên kết gồm hai thành phần:
• “data”: Lưu giữ dữ liệu.
• “next”: Con trỏ liên kết chỉ đến phần tử tiếp theo trong danh sách liên kết.

2. Duyệt trên danh sách liên kết

Bắt đầu từ phần tử đầu tiên ”head”, di chuyển đến các phần tử khác trên danh sách liên kết dựa vào biến con trỏ liên kết “next” như bên dưới.

3. Truy cập phần tử trong danh sách liên kết

Để truy cập phần tử thứ p trong danh sách liên kết, chúng ta phải di chuyển từ phần tử đầu tiên (head) cho đến phần tử thứ p như bên dưới.

4. Chèn một phần tử vào danh sách liên kết

Để chèn một phần tử có giá trị X vào vị trí p trong danh sách liên kết, chúng ta tiến hành hai bước cơ bản sau:
• Tạo một phần tử mới chứa giá trị X.
• Gán giá trị con trỏ liên kết “next” của phần tử thứ (p-1) và phần tử mới như bên dưới.

5. Xóa một phần tử khỏi danh sách liên kết

Để xóa phần tử tại vị trí p khỏi danh sách liên kết, chúng ta thay đổi giá trị con trỏ liên kết
“next” của phần tử thứ (p-1) như hình dưới. Sau đó xóa phần tử thứ p ra khỏi bộ nhớ!

6. Danh sách liên kết đôi

Danh sách liên kết được trình bày ở trên được gọi là danh sách liên kết đơn, bởi vì mỗi phần tử trong danh sách liên kết đơn có đúng một biến con trỏ liên kết “next” chỉ đến phần tử tiếp theo trong danh sách.Việc duyệt trên danh sách liên kết đơn chỉ thực hiện được theo một chiều từ đầu danh sách đến cuối danh sách và không thực hiện được theo chiều ngược lại.

Danh sách liên kết đôi là mở rộng của danh sách liên kết đơn, trong đó mỗi phần tử có hai con trỏ liên kết:
• “previous” trỏ đến phần tử phía trước trong danh sách.
• “next” trỏ đến phần tử tiếp theo trong danh sách.
Bên dưới là ví dụ minh họa sử dụng danh sách liên kết đôi để lưu giữ dãy A, B, C, D. Phần tử đầu tiên của danh sách liên kết đôi gọi là “head”, phần tử cuối cùng gọi là “tail”.

Danh sách liên kết đôi thường được dùng trong các bài toán cần duyệt trên danh sách theo cả hai chiều. Nó cũng hay được dùng trong các bài toán mà các phép toán chèn và xóa các phần tử trong danh sách thường diễn ra ở một trong hai đầu của danh sách bởi vì các phép toán này được thực hiện một cách nhanh chóng.

7. So sánh cấu trúc danh sách liên kết và cấu trúc mảng

• Các phần tử trong mảng được lưu giữ liên tục trong bộ nhớ, cho nên việc truy cập và thay đổi giá trị một phần tử bất kì trong mảng rất nhanh. Các phần tử trong danh sách liên kết được lưu giữ tại các vị trí khác nhau trong bộ nhớ máy tính, cho nên việc truy cập và thay đổi giá trị một phần tử bất kì tốn nhiều thời gian.
• Kích thước của mảng phải được xác định và khai báo trước khi sử dụng, điều này dẫn đến khó khăn và dư thừa bộ nhớ trong các bài toán mà số lượng các phần tử không được xác định trước. Khi sử dụng danh sách liên kết, chúng ta không phải khai báo trước kích thước của danh sách. Chúng ta chỉ xin cấp phát bộ nhớ khi thêm phần tử mới vào danh sách liên kết, và sẽ xóa phần tử đó khỏi bộ nhớ khi nó bị xóa khỏi danh sách liên kết.

C11PAIRS – spoj

Đề bài

Thuật toán

-Đầu tiên ta khởi tạo một stack để lưu chiều cao của các bạn theo thứ tự không tăng (s[i]<=s[i-1])

-Đầu tiên ta cho a[n] vào stack, khởi tạo ds=0,sn=1(sn là số phần tử trong ngăn xếp);

-Duyệt i từ n-1 về 1, với mỗi i:

+Tìm vị trí cuối cùng trong stacks mà s[i]>a[i], giả sử ta gọi là vt.

+Nếu vt=0 nghĩa là người đứng ở vị trí i có thể nhìn thấy mọi người trong stack vì vậy ds+=sn;

+Nếu vt!=0 nghĩa là người đứng ở vị trí i có thể nhìn thấy từ người thứ vt đến người thứ sn trong stack vì vậy ds+=(sn-vt+1);

+Tiếp theo ta sẽ bỏ các s[i]<a[i] trong stack sau đó thêm a[i] vào stack.

-Kết quả của bài này chính là ds.

-Từ đó ta thu được thuật toán với đpt O(n*log(n)), mặc dù có thể giảm đpt xuống còn O(n) nhưng mình thấy thuật toán này khá đơn giản và dễ code, có thể là ngắn hơn. Đây là code của mình:

Code

#include <bits/stdc++.h>
using namespace std;
#define FOR(i,a,b) for (int i=(a),_b=(b);i<=_b;i=i+1)
#define FORD(i,b,a) for (int i=(b),_a=(a);i>=_a;i=i-1)
#define REP(i,n) for (int i=0,_n=(n);i<_n;i=i+1)
#define FORE(i,v) for (__typeof((v).begin()) i=(v).begin();i!=(v).end();i++)
#define ALL(v) (v).begin(),(v).end()
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
#define double db
typedef long long ll;
typedef pair<int,int> PII;
const ll mod=1000000007;
ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
const int MAXN = 1E6+3;
const int oo = 1e9+3;
 
int n, i, j, a[MAXN], c[MAXN], s[MAXN], top, tmp;
ll ans;
 
int main() {
        ios_base::sync_with_stdio(0); cin.tie(0);
    	#ifndef ONLINE_JUDGE
    	freopen("test.inp", "r", stdin);
    	freopen("test.out", "w", stdout);
    	#endif //yeulaptrinh.pw
    cin >> n;
    FOR(i,1,n) cin >> a[i];
 
    top = 0;
    FOR(i,1,n) {
        tmp = 1;
        while (top&&s[top]<=a[i]) {
            ans += c[top];
            if (s[top]==a[i]) tmp += c[top];
            c[top] = 0; top--;
        }
        if (top) ans++;
        // push
        top++;
        s[top] = a[i];
        c[top] = tmp;
    }
    cout << ans;
	return 0;
}

NKINV – spoj

Đề bài

Thuật toán

  • Interval Tree

Code

uses    math;
const   fi='';
        fo='';
        maxn = 60000+3;
var     t       :array[1..maxn*4] of longint;
        i,j,n   :longint;
        a       :ARRAY[1..maxn] of longint;
        ma      :longint;
        res     :int64;
procedure enter;
begin
        assign(input,fi);reset(input);
        readln(n);for i:=1 to n do read(a[i]);
        close(input);
end;
procedure update(k,l,r,i:longint);
var     m :longint;
begin
        if (i<l) or (i>r) then exit;
        if l=r then
                begin
                        inc(t[k]);
                        exit;
                end;
        m := (l+r) div 2;
        update(k*2,l,m,i);
        update(k*2+1,m+1,r,i);
        t[k] := t[k*2] + t[k*2+1];
end;
function get(k,l,r,i,j:longint):longint;
var     m,tam1,tam2     :longint;
begin
        if i>j then exit(0);
        if (i>r) or (j<l) then exit(0);
        if (i<=l) and (j>=r) then
                begin
                        exit(t[k]);
                end;
        m := (l+r) div 2;
        tam1 := get(k*2,l,m,i,j);
        tam2 := get(k*2+1,m+1,r,i,j);
        get:= tam1+tam2;
end;
procedure process;
var     i :longint;
begin
        for i:=1 to n do ma := max(ma,a[i]);
        res := 0;
        for i:=1 to n do
                begin
                        res := res + get(1,1,ma,a[i]+1,ma);
                        update(1,1,ma,a[i]);
                end;
end;
procedure print;
begin
        assign(output,fo);rewrite(output);
        writeln(res);
        close(output);
end;
begin
        enter;
        process;
        print;
end.

PCIRCLE – spoj

Đề bài:

Thuật toán:

  • Duyệt cấu hình tổ hợp

Code:

uses    math;
 
const   fi='';
        fo='';
        maxn=10;
 
type    data=longint;
 
var     x       :array[1..2*maxn] of longint;
        i,j,n,m :longint;
        st      :array[1..2*maxn] of longint;
        kq      :array[1..10001,1..2*maxn] of longint;
        res     :int64;
        cx      :array[1..2*maxn] of boolean;
 
procedure enter;
begin
        assign(input,fi);reset(input);
        readln(n);
        close(input);
end;
 
function ngto(x : longint) : boolean;
var     i       :longint;
begin
        for i:=2 to trunc(sqrt(x)) do
                if x mod i = 0 then exit(false);
        exit(true);
end;
 
procedure cnkl;
begin
        if ngto(x[2*n]+x[1]) then
                begin
                        inc(res);
                        if res<=10000 then
                        kq[res] := x;
                end;
end;
 
procedure try(i : longint);
var     j :longint;
begin
        for j:= 1 to 2*n do
                if cx[j] then
                        if ngto(j+x[i-1]) then
                        begin
                                x[i] := j;
                                cx[j] := false;
                                if i=2*n then cnkl else try(i+1);
                                cx[j] := true;
                        end;
end;
procedure process;
begin
        {for i:=2 to 2*n do
                if ngto(i) then
                        begin
                                inc(m);
                                st[m] := i;
                        end;     }
        fillchar(cx,sizeof(cx),true);
        cx[1] := false;
        x[1] := 1;
        try(2);
end;
 
procedure print;
begin
        assign(output,fo);rewrite(output);
        writeln(res);
        for i:=1 to min(res,10000) do
                begin
                        for j:=1 to 2*n do write(kq[i,j],' ');
                        writeln;
                end;
        close(output);
end;
 
begin
        enter;
        process;
        print;
end.

QBSCHOOL – spoj

Đề bài:

Thuật toán:

  • Gọi Gọi f[i] và d[i] lần lượt là độ dài và số đường đi ngắn nhất từ đỉnh 1 đến đỉnh i.
  • Để tính d[i] ta sử dụng thuật toán Dijkstra
  • Để tính số lượng đường đi ngắn nhất giữa hai đỉnh ta tính như sau
  • Nếu d[v]>d[u]+c(u,v), ta gán d[v]=d[u]+c(u,v) và f[v]=f[u]

    Nếu d[v]=d[u]+c(u,v) thì gán f[v]=f[v]+f[u].

    (có thể xem rõ hơn ở code bên dưới)

Code:

const   fi      ='';
        fo      ='';
        maxn    =30000+3;
        maxc    =trunc(1e9)+3;
        maxm    =trunc(1e5);
type    arrd    =array[1..maxn] of longint;
        arrf    =array[1..maxn] of int64;
        arrk    =array[-maxm..maxm] of longint;
        arrh    =array[1..maxn] of longint;
var     d1,dn,d   :arrd;
        f1,fn,f   :arrf;
        n,i,j,m   :longint;
        res     :longint;
        ans     :arrd;
        head :array[1..maxn] of longint;
        link,ts,ke    :arrk;
        // dijkstra heap;
        h,p     :array[1..maxn] of longint;
        nh      :longint;
procedure add(i,u,v,w:longint);
begin
        link[i]:=head[u];
        head[u]:=i;
        ke[i]:=v;
        ts[i]:=w;
end;
procedure enter;
var     u,v,w,k:longint;
begin
        assign(input,fi);reset(input);
        readln(n,m);
        for i:=1 to m do
                begin
                        read(k,u,v,w);
                        add(i,u,v,w);
                        if k=2 then add(-i,v,u,w);
                end;
        close(input);
end;
procedure swap(var x,y:longint);
var     tg:longint;
begin
        tg:=x;x:=y;y:=tg;
end;
procedure upheap(i:longint);
var     j:longint;
begin
        j:=i div 2;
        if i>1 then
        if d[h[i]] < d[h[j]] then
                begin
                        swap(h[i],h[j]);
                        swap(p[h[i]],p[h[j]]);
                        upheap(j);
                end;
end;
procedure downheap(i:longint);
var     j:longint;
begin
        j:=i + i;
        if j>nh then exit;
        if (j<nh) and (d[h[j]] > d[h[j+1]]) then inc(j);
        if d[h[j]] < d[h[i]] then
                begin
                        swap(h[i],h[j]);
                        swap(p[h[i]],p[h[j]]);
                        downheap(j);
                end;
end;
procedure push(i:longint);
begin
        inc(nh);
        h[nh]:=i;
        p[i]:=nh;
        upheap(nh);
end;
function pop:longint;
begin
        pop:=h[1];
        h[1]:=h[nh];
        p[h[1]]:=1;
        dec(nh);
        downheap(1);
end;
procedure update(i:longint);
begin
        if p[i]=0 then push(i) else upheap(p[i]);
end;
procedure dijkstra(s:longint);
var     u,v,i:longint;
begin
        fillchar(h,sizeof(f),0);
        fillchar(p,sizeof(p),0);
        nh:=0;
        fillchar(f,sizeof(f),0);
        for i:=1 to n do d[i]:=maxc;
        d[s]:=0;
        f[s]:=1;
        push(s);
        repeat
                u:=pop;
                i:=head[u];
                while i<>0 do
                        begin
                                v:=ke[i];
                                if d[v]>d[u]+ts[i] then
                                        begin
                                                d[v]:=d[u]+ts[i];
                                                f[v]:=f[u];
                                                update(v);
                                        end
                                        else
                                if d[v]=d[u]+ts[i] then
                                        begin
                                                f[v]:=f[u]+f[v];
                                        end;
                                i:=link[i];
                        end;
        until nh=0;
end;
procedure process;
begin
        dijkstra(1);
        f1:=f;
        d1:=d;
        dijkstra(n);
        fn:=f;
        dn:=d;
        for i:=1 to n do
                if (d1[i]+dn[i]<>d1[n])
                or (f1[i]*fn[i]<>f1[n]) then
                        begin
                                inc(res);
                                ans[res]:=i;
                        end;
end;
procedure print;
begin
        assign(output,fo);rewrite(output);
        writeln(d1[n],' ',f1[n]);
        close(output);
end;
begin
        enter;
        process;
        print;
end.
#include <iostream>
#include <queue>
#include <vector>
#include <cstring>
 
using namespace std;
 
typedef pair<long long, int> lli;
 
const long long oo = ((1 << 30)-1)*2-1;
const int max_n = 5000+1;
 
int n, m;
long long min_d, dist = 0, f[max_n];
vector<lli> adj[max_n];
 
inline void load_map()
{
    cin >> n >> m;
    int type, u, v, w;
    while (m--)
    {
        cin >> type >> u >> v >> w;
        adj[u].push_back(lli(w, v));
        if (type==2) adj[v].push_back(lli(w, u));
    }
}
 
inline void dijkstra()
{
    vector<lli>::iterator it;
    long long cuv, du, d[max_n];
    int u, v;
    priority_queue<lli, vector<lli>, greater<lli> > heap;
    for (u=1; u <= n; u++) d[u] = +oo;
    memset(f, 0, sizeof(f));
    d[1] = 0; f[1] = 1;
    heap.push(lli(0, 1));
    while (heap.size())
    {
        du = heap.top().first; u = heap.top().second;
        heap.pop();
        if (du != d[u]) continue;
        if (u == n) break;
        for (it=adj[u].begin(); it != adj[u].end(); it++)
        {
            cuv = it->first; v = it->second;
            if (d[v]==d[u]+cuv) f[v] += f[u];
            if (d[v] > d[u]+cuv)
            {
                d[v] = d[u]+cuv;
                f[v] = f[u];
                heap.push(lli(d[v], v));
            }
        }
    }
    min_d = d[n];
}
 
main()
{
    load_map();
    dijkstra();
    cout << min_d << " " << f[n];
}

LUBENICA – spoj

Đề bài:

Thuật toán:

  • LCA

Code:

uses    math;
const   fi='';
        fo='';
        maxk=20;
        maxn=trunc(1e5)+3;
        oo=2*trunc(1e9);
 
type    arr1    =array[1..maxn] of longint;
        arr2    =array[1..maxn,0..maxk] of longint;
 
var     mi,ma,p         :arr2;
        depth,dad       :arr1;
        i,j,n,m,q       :longint;
        link,head,ke,ts :array[-maxn..maxn] of longint;
        resma,resmi     :longint;
        cx      :array[1..maxn] of boolean;
procedure add(i,u,v,w:longint);
begin
        link[i]:=head[u];
        head[u]:=i;
        ke[i]:=v;
        ts[i]:=w;
end;
 
procedure enter;
var     u,v,w   :longint;
begin
        readln(n);
        for i:=1 to n-1 do
        begin
                read(u,v,w);
                add(i,u,v,w);
                add(-i,v,u,w);
        end;
end;
 
procedure dfs(u:longint);
var     i,v     :longint;
begin
        cx[u]:=false;
        i := head[u];
        while i<>0 do
                begin
                        v:=ke[i];
                        if cx[v] then
                                begin
                                        depth[v]:=depth[u]+1;
                                        dad[v]:=u; ma[v,0] := ts[i]; mi[v,0] := ts[i];
                                        dfs(v);
                                end;
                        i:=link[i];
                end;
end;
 
procedure init;
var     i,j     :longint;
begin
        fillchar(cx,sizeof(cx),true);
        dad[1] := 1; depth[1] :=1;
        dfs(1);
        for i:=1 to n do
                begin
                        p[i,0] := dad[i];
                end;
        for j:=1 to maxk do mi[1,0] := oo;
        for j:=1 to maxk do ma[1,0] := -oo;
        for j:=1 to maxk do
                for i:=1 to n do
                        begin
                                p[i,j] := p[p[i,j-1],j-1];
                                ma[i,j] := max(ma[i,j-1],ma[p[i,j-1],j-1]);
                                mi[i,j] := min(mi[i,j-1],mi[p[i,j-1],j-1]);
                        end;
end;
procedure lca(u,v:longint);
var     i,j     :longint;
begin
        for i:=maxk downto 0 do
                if depth[p[u,i]]>=depth[v] then
                        begin
                                resma := max(resma,ma[u,i]);
                                resmi := min(resmi,mi[u,i]);
                                u := p[u,i];
                        end;
        for i:=maxk downto 0 do
                if depth[p[v,i]]>=depth[u] then
                        begin
                                resma := max(resma,ma[v,i]);
                                resmi := min(resmi,mi[v,i]);
                                v := p[v,i];
                        end;
        if u=v then exit;
        for i:=maxk downto 0 do
                if p[u,i]<>p[v,i] then
                        begin
                                resma := max(resma,ma[v,i]);
                                resmi := min(resmi,mi[v,i]);
                                resma := max(resma,ma[u,i]);
                                resmi := min(resmi,mi[u,i]);
                                v := p[v,i];
                                u := p[u,i];
                        end;
        resma := max(resma,ma[v,0]);
        resmi := min(resmi,mi[v,0]);
        resma := max(resma,ma[u,0]);
        resmi := min(resmi,mi[u,0]);
end;
procedure process;
var     u,v,qq  :longint;
begin
        read(q);
        for qq:=1 to q do
                begin
                        read(u,v);
                        if u=v then begin writeln(0,' ',0); continue; end;
                                resmi := oo;
                                resma := -oo;
                                lca(u,v);
                        writeln(resmi,' ',resma);
                end;
end;
procedure print;
begin
 
end;
 
begin
        assign(input,fi);reset(input);
        assign(output,fo);rewrite(output);
                enter;
                init;
                process;
                print;
        close(input);close(output);
end

VOTREE – spoj

Đề bài:

Giải thích ví dụ

votree-yeulaptrinh
Truy vấn 1: Tìm tổ tiên chung gần nhất của các đỉnh được đánh số từ 2 đến 5, ở đây có thể thấy 2 là tổ tiên chung gần nhất của các đỉnh 2,3,4,5.
Truy vấn 2: Tìm tổ tiên chung gần nhất của các đỉnh được đánh số từ 1 đến 3, ở đây có thể thấy 1 là tổ tiên chung gần nhất của các đỉnh 1,2,3.
Truy vấn 3: Tìm tổ tiên chung gần nhất của các đỉnh được đánh số từ 4 đến 5, ở đây có thể thấy 3 là tổ tiên chung gần nhất của các đỉnh 4,5.

Thuật toán:

    Có thể nhận thấy rằng tổ tiên chung gần nhất ( LCA ) của 3 đỉnh a,b,c sẽ bằng với LCA(a,LCA(c,b)). Dựa trên tính chất nãy ta có thể sử dụng Interval Tree để tìm LCA của 1 đoạn liên tiếp bằng cách tổ chức cây IT sẽ lưu LCA của các phần tử trong đoạn quản lý.

    Các phần cơ bản của cây IT:

    IT[id] = phần tử đang được quản lý khi đoạn quản lý gồm 1 phần tử.

    IT[id] = LCA(IT[id*2],IT[id*2+1] ).

    P/s : Có rất nhiều cách để tìm LCA ví dụ như : Sparse Table, Heavy Light Decomposition, Interval Tree, …

Code:

const
        tfi='';
        tfo='';
 
var
        fi,fo:Text;
        ke,nx:array[-70000..70000] of longint;
        num,thoat,hd:array[0..70000] of longint;
        f:array[0..70000,0..20] of longint;
        n,q,jmax,dem,dem1:longint;
        t:array[0..3000000] of longint;
 
procedure nhap;
        var i,j,u,v:longint;
        begin
                read(fi,n,q);
                for i:=1 to n-1 do
                        begin
                                read(fi,u,v);
                                ke[i]:=v;
                                nx[i]:=hd[u];
                                hd[u]:=i;
                                ke[-i]:=u;
                                nx[-i]:=hd[v];
                                hd[v]:=-i;
                        end;
                for j:=1 to 20 do if 1 shl j<=n then jmax:=j+1;
        end;
 
procedure DFS(u:longint);
        var j,v:longint;
        begin
                inc(dem);
                num[u]:=dem;
                for j:=1 to jmax do f[u,j]:=f[f[u,j-1],j-1];
                j:=hd[u];
                while j<>0 do
                        begin
                                v:=ke[j];
                                if f[v,0]=0 then
                                        begin
                                                f[v,0]:=u;
                                                DFS(v);
                                        end;
                                j:=nx[j];
                        end;
                inc(dem1);
                thoat[u]:=dem1;
        end;
 
function cha(x,y:longint):boolean;
        begin
            cha:=(num[x]<=num[y]) and (thoat[y]<=thoat[x]);
        end;
 
function lca(x,y:longint):longint;
        var j:longint;
        begin
                if cha(x,y) then exit(x);
                if cha(y,x) then exit(y);
                for j:=jmax downto 0 do
                        if not cha(f[x,j],y) then x:=f[x,j];
                exit(f[x,0]);
        end;
 
procedure init(i,l,r:longint);
        var mid:longint;
        begin
            if l=r then
                begin
                        t[i]:=l;
                        exit;
                end;
            mid:=(l+r) div 2;
            init(i*2,l,mid);
            init(i*2+1,mid+1,r);
            t[i]:=lca(t[i*2],t[i*2+1]);
        end;
 
function get(i,l,r,u,v:longint):longint;
        var mid:longint;
        begin
                if (l>v) or (r<u) then exit(u);
                if (u<=l) and (r<=v) then exit(t[i]);
                mid:=(l+r) div 2;
                get:=lca(get(i*2,l,mid,u,v),get(i*2+1,mid+1,r,u,v));
        end;
 
procedure xuli;
        var i,u,v,pa,j:longint;
        begin
                f[1,0]:=1;
                DFS(1);
                init(1,1,n);
                for i:=1 to q do
                        begin
                                read(fi,u,v);
                                writeln(fo,get(1,1,n,u,v));
                        end;
 
        end;
 
begin
        assign(fi,tfi);
        assign(fo,tfo);
        reset(fi);
        rewrite(fo);
        nhap;
        xuli;
        close(fo);
end.