數據結構總結1(火車進棧/兔子與兔子/括號畫家/Editor/鄰值查找/最大子序和/前綴統計/Phone List/The XOR Largest Pair)

問題 A: 火車進棧

題目描述

這裏有n列火車將要進站再出站……
但是,每列火車只有1節—那就是車頭……
描述
有n列火車按1到n的順序從東方左轉進站,這個車站是南北方向的,它雖然無限長,只可惜是一個死衚衕,而且站臺只有一條股道,火車只能倒着從西方出去,而且每列火車必須進站,先進後出。
(某生:不就是個棧嗎?每次可以讓右側頭火車進棧,或者讓棧頂火車出站?
老師:閉嘴!)
就像這樣:
出站<——- <——進站
|車|
|站|
|__|
現在請你按《字典序》輸出前20種可能的出棧方案。

輸入

一個整數 n<=20

輸出

按照《字典序》輸出前20種答案,每行一種,不要空格

樣例輸入 Copy

3

樣例輸出 Copy

123
132
213
231
321

思路
依題意可知,火車只有入棧和出棧兩個操作,且所有火車都需要進行這兩種操作,使用STL中的stack來模擬該操作。使用visit[ ]數組來判斷火車是否已經入棧,注意只有當第i-1個火車入棧後,第i個火車才能入棧。使用dfs的寫法即可完成此題。

#include <iostream>
#include <stack>
#include <vector>
using namespace std;

int n;
bool visit[25];///已經如果棧的標誌
stack<int> s;
vector<int>ans;
int cnt=0;
void solve()
{
    if(cnt>=20) return;
    if(ans.size()==n)///搜索邊界,輸出答案
    {
        for(int i=0;i<n;i++)cout<<ans[i];
        cout<<endl;
        cnt++;
        return;
    }
    if(!s.empty())///棧內不爲空,可以出棧
    {
        int x=s.top();
        ///出棧
        ans.push_back(x);
        s.pop();
        solve();///棧頂出棧後,向下一層搜索
        ///還原狀態
        ans.pop_back();
        s.push(x);
    }
    for(int i=1;i<=n;i++)
    {
        if(!visit[i]&&visit[i-1])///第i輛未入棧,i-1輛已經入過棧
        {
            visit[i]=true;
            s.push(i);
            solve();
            ///狀態恢復,回溯
            visit[i]=false;
            s.pop();
        }
    }
    return;
}

int main()
{
    visit[0]=true;
    cin>>n;
    solve();
    return 0;
}

問題 I: 兔子與兔子

題目描述

很久很久以前,森林裏住着一羣兔子。有一天,兔子們想要研究自己的 DNA 序列。我們首先選取一個好長好長的 DNA 序列(小兔子是外星生物,DNA 序列可能包含 26 個小寫英文字母),然後我們每次選擇兩個區間,詢問如果用兩個區間裏的 DNA 序列分別生產出來兩隻兔子,這兩個兔子是否一模一樣。注意兩個兔子一模一樣只可能是他們的 DNA 序列一模一樣。

輸入

第一行一個 DNA 字符串 S。
接下來一個數字 m,表示 m 次詢問。
接下來 m 行,每行四個數字 l1, r1, l2, r2,分別表示此次詢問的兩個區間,注意字符串的位置從1開始編號。
其中 1 ≤ length(S), m ≤ 1000000

輸出

對於每次詢問,輸出一行表示結果。如果兩隻兔子完全相同輸出 Yes,否則輸出 No(注意大小寫)

樣例輸入 Copy

aabbaabb
3
1 3 5 7
1 3 6 8
1 2 1 2

樣例輸出 Copy

Yes
No
Yes

思路
字符串hash求解即可
字符串hash函數:
對於字符串C,使用質數b,及模數h來計算hash函數
H( C )=[ c1 * bm + c2 * b(m-1) + . . . + cn*b1 ] mod h
可以理解爲字符串講C可以轉化爲一個m位b進制的數
注意
實際用32/64位無符號整數來計算哈希值,這樣的話,當哈希值溢出時,系統會自動對h=232 / h=264取模。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

char s[1000005];
unsigned long long sum[1000005],bit[1000005]={1};///注意bit[1]=131
///用32/64位無符號整數來計算哈希值,當哈希值溢出時,系統會自動對h=2^32 / h=2^64取模。
int main()
{
    scanf("%s",s+1);
    int len=strlen(s+1);
    for(int i=1;i<=len;i++)
    {
        sum[i]=sum[i-1]*131+s[i]-'a'+1;
        bit[i]=bit[i-1]*131;
    }
    int n;
    cin>>n;
    while(n--)
    {
        int l1,r1,l2,r2;
        scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
        if(sum[r1]-sum[l1-1]*bit[r1-l1+1]==sum[r2]-sum[l2-1]*bit[r2-l2+1])
        ///注意乘以bit[r1-l1+1],將兩個數轉化位位數相同
            printf("Yes\n");
        else
            printf("No\n");
    }
    return 0;
}

問題 S: 括號畫家

題目描述

Candela是一名漫畫家,她有一個奇特的愛好,就是在紙上畫括號。這一天,剛剛起牀的Candela畫了一排括號序列,其中包含小括號( )、中括號[ ]和大括號{ },總長度爲N。這排隨意繪製的括號序列顯得雜亂無章,於是Candela定義了什麼樣的括號序列是美觀的:
(1) 空的括號序列是美觀的;
(2) 若括號序列A是美觀的,則括號序列 (A)、[A]、{A} 也是美觀的;
(3) 若括號序列A、B都是美觀的,則括號序列AB也是美觀的。
例如 (){} 是美觀的括號序列,而 )({)[}]( 則不是。
現在Candela想在她繪製的括號序列中,找出其中連續的一段,滿足這段子序列是美觀的,並且長度儘量大。你能幫幫她嗎?S

輸入

一個由括號組成的字符串。

輸出

一個整數,表示最長的美觀的子段的長度。

樣例輸入 Copy

({({(({()}})}{())})})[){{{([)()((()]]}])[{)]}{[}{)

樣例輸出 Copy

4

思路
1.首先讀題知道如果一個左括號c1,向右查詢得到的第一個右括號c2與c1匹配的話,那麼這兩個括號是合法的,否則非法。
2.那麼可以使用棧stack來模擬括號匹配:
1)爲左括號則入棧
2)否則判斷右括號是否和棧頂的左括號匹配,若匹配則計數器cnt+=2,更新答案ans,且彈棧,若不匹配則cnt=0,清棧。

#include <iostream>
#include <stack>
#include <cstdio>
#include <cstring>
using namespace std;
stack<char> sta;
char s[100005];
bool judge(char a,char b)
{
    if(a=='('&&b==')') return true;
    if(a=='['&&b==']') return true;
    if(a=='{'&&b=='}') return true;
    return false;
}
int main()
{
    scanf("%s",s);
    int ans=0,cnt=0;
    for(int i=0;i<strlen(s);i++)
    {
        if(s[i]=='('||s[i]=='['||s[i]=='{')
            sta.push(s[i]);
        else
        {
            char c='a';
            if(!sta.empty())c=sta.top();
            if(judge(c,s[i]))
            {
                cnt+=2;
                ans=max(cnt,ans);
                sta.pop();
            }
            else
            {
                cnt=0;
                while(!sta.empty()) sta.pop();
            }
        }
    }
    cout << ans << endl;
    return 0;
}


問題 C: Editor

題目描述

You are going to implement the most powerful editor for integer sequences.
The sequence is empty when the editor is initialized.
There are 5 types of instructions.

I x Insert x after the cursor.
D Delete the element before the cursor.
L Move the cursor left unless it has already been at the begin.
R Move the cursor right unless it has already been at the end.
Q k Suppose that the current sequence BEFORE the cursor is {a1,a2,…,an}.Find max1≤i≤k Si where Si=a1+a2+…+ai.

輸入

The input file consists of multiple test cases.For eache test case:
The first line contains an integer Q,which is the number of instructions.The next Q lines contain an instruction as described above。
(1≤Q≤106,|x|≤103 for I instruction,1≤k≤n for Q instruction)

輸出

For eache Q instruction,output the desired value.

樣例輸入 Copy

8
I 2
I -1
I 1
Q 3
L
D
R
Q 2

樣例輸出 Copy

2
3

思路
使用對頂棧,用L來表示光標左邊的數據,R表示光標右邊的數據,LR的一彈棧一入棧來模擬光標移動。並且使用數組num來維護前 i 項和,數組maxn來維護前 i 項和的最大值

#include <iostream>
#include <cstdio>
#include <stack>
#include <vector>
using namespace std;

///通過LR的彈棧和入棧操作模擬光標的移動
stack<int>L,R;///對頂堆,L爲光標左邊的堆,R爲光標右邊的
vector<int>num,maxn;///num爲前i項和,maxn爲前i項和中的最大值
int n,x;
char op;

int main()
{
    num.push_back(0);///初始化爲0
    maxn.push_back(-0xffffff);///初始化爲最小值,以爲數列中有負值
    cin>>n;
    getchar();
    while(n--)
    {
        cin>>op;
        if(op=='I')///插入操作,則L入棧,更新前n項和及其最大值
        {
            cin>>x;
            L.push(x);
            num.push_back(num[num.size()-1]+x);
            maxn.push_back(max(num[num.size()-1],maxn[maxn.size()-1]));
        }
        if(op=='D')///刪除操作,L彈棧,前n項和也更新彈出最後一項
        {
            if(!L.empty())
            {
                L.pop();
                num.pop_back();
                maxn.pop_back();
            }
        }
        if(op=='L')///光標左移,則L彈棧,R入棧
        {
            if(!L.empty())///注意要L非空
            {
                R.push(L.top());
                L.pop();
                num.pop_back();///前n項和彈出最後一項
                maxn.pop_back();
            }
        }
        if(op=='R')///光標右移動,同上
        {
            if(!R.empty())
            {
                L.push(R.top());
                ///前n項和更新
                num.push_back(R.top()+num[num.size()-1]);
                maxn.push_back(max(num[num.size()-1],maxn[maxn.size()-1]));
                R.pop();
            }
        }
        if(op=='Q')///詢問
        {
            scanf("%d",&x);
            cout<<maxn[x]<<'\n';
        }
    }
    return 0;
}


問題 H: 鄰值查找

題目描述

給定一個長度爲 n 的序列 A,A 中的數各不相同。對於 A 中的每一個數 Ai,求:
min(1≤j<i) ⁡|Ai-Aj|
以及令上式取到最小值的 j(記爲 Pi)。若最小值點不唯一,則選擇使 Aj 較小的那個。

輸入

第一行一個整數n,第二行n個數A_1~A_n。

輸出

n-1行,每行2個用空格隔開的整數。分別表示當i取2~n時,對應的 min(1≤j<i) ⁡|A_i-A_j| 和 P_i 的值。

樣例輸入 Copy

3
1 5 3

樣例輸出 Copy

4 1
2 1

提示

對於30%的數據: n<=100
對於70%的數據: n<=10^4
對於100%的數據: n<=10^5, |A_i|<=10^9

思路
1)數據結構的選擇
由題意可以知道後面加入的數x,要去找到前面和他絕對值差值最小的數y(y有兩種情況,y>x,y<x)然後插入x。這個插入的過程類似與平衡二叉樹的建樹過程,所以可以使用平衡二叉樹來進行數據的維護。然而c++中STL的set就是使用紅黑樹(自平衡查找二叉樹)寫的,使用set進行數據維護即可,並且set有lower_bound(x)函數來查找<x的最大的數據(當然要重載<)。
2)每次插入前,去找到當前這個數字最接近的值的前驅和後驅,比較前驅和後驅,找到最小的j,
如果找不到大於等於這個數字,能麼需要判斷end()(注意stl中全部都是以左閉右開的形式來保存的),去直接輸出end()-1,同理如果是begin(),直接輸出begin()+1

#include <iostream>
#include <set>
#include <cstdio>
#include <cmath>
using namespace std;

struct node
{
    int num,id;
    bool operator<(const node &a) const
    {
        return num<a.num;
    }
};
set<node>s;

int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        int tmp;
        scanf("%d",&tmp);
        if(i==1) 
            {s.insert(node{tmp,i});continue;}
        set<node>::iterator right=s.lower_bound(node{tmp,0}),left=right;left--;
        if(right==s.end())
            printf("%d %d\n",tmp-left->num,left->id);
        else if(right==s.begin())
            printf("%d %d\n",right->num-tmp,right->id);
        else if((tmp-left->num)<=(right->num-tmp))
            printf("%d %d\n",tmp-left->num,left->id);
        else
            printf("%d %d\n",right->num-tmp,right->id);
        s.insert(node{tmp,i});
    }
    return 0;
}


問題 E: 最大子序和

題目描述

輸入一個長度爲n的整數序列,從中找出一段不超過m的連續子序列,使得整個序列的和最大。

例如 1,-3,5,1,-2,3

當m=4時,S=5+1-2+3=7
當m=2或m=3時,S=5+1=6

輸入

第一行兩個數n,m(n,m<=300000)
第二行有n個數,要求在n個數找到最大子序和

輸出

一個數,數出他們的最大子序和

樣例輸入 Copy

6 4
1 -3 5 1 -2 3

樣例輸出 Copy

7

使用的數據結構
單調隊列

單調遞增隊列的創建過程(隊首到隊尾單調遞增):
若隊列爲空,將元素x從隊尾入隊
若隊列不爲空,將比元素x大的元素都從隊尾彈出,然後把元素x入隊
若隊列不爲空且元素x大於隊尾,則直接從隊尾把元素x入隊

**注意**:單調遞增隊列維護的是區間最小值,而單調遞減隊列維護的是最區間大值
兩個隊列都是對首維護的是最小值/最大值

值得一提的是:單調棧的創建過程同單調隊列,其出棧的序列是單調的

注意
單調隊列的單調不一定是數字大小順序上的單調,可以將判斷順序的依據重載,根據題目所需重新定義排序規則

思路
使用單調隊列求解(用雙端隊列實現,因爲還需要判斷區間長度是否大於m,隊首隊尾都要訪問,且都要可以出隊)
求解過程:
1)求出前綴和以便求出區間和
2)因爲要判斷區間長度是否大於m,所以單調遞增隊列儲存的是下標,維護的是區間[i-m,i]最小值的序號q.front(),那麼sum[i]-sum[q.front()]即爲區間[i-m,i]的最大值。

#include <iostream>
#include <deque>
using namespace std;

deque<int> q;
long long sum[300005];
long long maxn;

void solve(int n,int m)
{
    maxn=sum[1];
    q.push_back(1);
    for(int i=2;i<=n;i++)
    {
        while(!q.empty()&&i-q.front()>m)///去除區間長度大於m的隊尾元素
            q.pop_front();
        maxn=max(maxn,sum[i]-sum[q.front()]);///更新最大值
        while(!q.empty()&&sum[i]<sum[q.back()])///單調隊列更新
            q.pop_back();
        q.push_back(i);///入隊
    }
}

int main()
{
    int n,m;cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        int x;cin>>x;
        sum[i]=sum[i-1]+x;
    }
    solve(n,m);
    cout<<maxn<<endl;
    return 0;
}


問題L 前綴統計

題目描述:
給定N個字符串S1,S2…SN,接下來進行M次詢問,每次詢問給定一個字符串T,求S1~SN中有多少個字符串是T的前綴。

輸入字符串的總長度不超過106,僅包含小寫字母。

輸入格式

第一行輸入兩個整數N,M。接下來N行每行輸入一個字符串Si。接下來M行每行一個字符串T用以詢問。

輸出格式

對於每個詢問,輸出一個整數表示答案。每個答案佔一行。

輸入樣例:

3 2
ab
bc
abc
abc
efg

輸出樣例:

2
0

思路
裸的字典樹(26叉樹)模板,參考博客

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

struct node
{
    int sum;
    node* c[26];
    node()
    {
    	memset(c,NULL,sizeof(c));
    	sum=0;
    }
}* root=new node;
int n,m;
char str[1000005];

void Insert(const char *s)///trie樹插入操作
{
    node *e=root;
    while(*s)
    {
        if(!e->c[*s-'a'])
            e->c[*s-'a']=new node;
        e=e->c[*s++ -'a'];
    }
    ++(e->sum);
}

int query(char *s)///trie樹查詢操作
{
    int res=0;
    node *e=root;
    while(*s&&e)
    {
        res+=e->sum;
        e=e->c[*s++ -'a'];
    }
    if(e)///注意*s=='\0'時,e可能!=null
        res+=e->sum;
    return res;
}

int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        scanf("%s",str);
        Insert(str);
    }
    long long ans=0;
    for(int i=1;i<=m;i++)
    {
        scanf("%s",str);
        cout<<query(str)<<'\n';
    }
    return 0;
}

問題 AB: 【Trie 字典樹】Phone List

題目描述

Given a list of phone numbers, determine if it is consistent in the sense that no number is the prefix of another. Let’s say the phone catalogue listed these numbers:
• Emergency 911
• Alice 97 625 999
• Bob 91 12 54 26
In this case, it’s not possible to call Bob, because the central would direct your call to the emergency line as soon as you had dialled the first three digits of Bob’s phone number. So this list would not be consistent.

輸入

The first line of input gives a single integer, 1 ≤ t ≤ 40, the number of test cases. Each test case starts with n, the number of phone numbers, on a separate line, 1 ≤ n ≤ 10000.
Then follows n lines with one unique phone number on each line. A phone number is a sequence of at most ten digits.

輸出

For each test case, output “YES” if the list is consistent, or “NO” otherwise.

樣例輸入 Copy

2
3
911
97625999
91125426
5
113
12340
123440
12345
98346

樣例輸出 Copy

NO
YES

思路
先進行插入操作,建樹,然後遍歷所有的字符串str[i],在樹上找到該字符串str[i]的最後一個節點,查詢其是否有子節點,若有則輸出NO,若遍歷後都沒有則輸出YES。注意每組操作結束後記得將樹刪除。
注意
節點的構造函數記得寫,還有題目沒給出每個字符串的長度所有使用動態的字符串string。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <stack>
using namespace std;

string str[10005];///沒給出具體長度,使用string
struct node
{
    node *c[10];
    node()
    {
        memset(c,NULL,sizeof(c));///不能忘,否則會出錯
    }
}* root;

void Insert(string s)
{
    node *e=root;
    int len=s.size();
    for(int i=0;i<len;i++)
    {
        if(!e->c[s[i]-'0'])
            e->c[s[i]-'0']=new node;
        e=e->c[s[i]-'0'];
    }
}

void Delete(node *e)
{
    for(int i=0;i<10;i++)
        if(e->c[i])
            Delete(e->c[i]);
    if(e) delete e;
}

bool query(string s)
{
    node *e=root;
    int len=s.size();
    for(int i=0;i<len;i++)
        e=e->c[s[i]-'0'];
    for(int i=0;i<=9;i++)
        if(e->c[i]) return false;
    return true;
}

int main()
{
    int t;cin>>t;
    while(t--)
    {
        root=new node;
        int n;cin>>n;
        for(int i=1;i<=n;i++)
        {
            cin>>str[i];
            Insert(str[i]);
        }
        int sign=0;
        for(int i=1;i<=n;i++)
        {
            if(!query(str[i]))
            {
                sign=1;
                break;
            }
        }
        if(sign) cout<<"NO"<<endl;
        else cout<<"YES"<<endl;
        Delete(root);
    }
    return 0;
}


問題 M: 【Trie 字典樹】The XOR Largest Pair

題目描述

在給定的N個整數A1,A2……AN中選出兩個進行xor運算,得到的結果最大是多少?

輸入

第一行一個整數N,第二行N個整數A1~AN。

輸出

一個整數表示答案。

樣例輸入 Copy

3
1 2 3

樣例輸出 Copy

3

提示

對於100%的數據: N<=105, 0<=Ai<231

思路
將數按二進制表示從高位到低位插入到字典樹中(這樣可以保證數查詢時儘量大,因爲高位異或得到的1的權值大於低位異或得到的1的權值),然後每個數查詢時往與當前位相反的樹的分支查詢。如:a[1…4]=1001,a[1]查詢時往樹0的分支走。

代碼1
使用change()函數將數轉化爲2進制,位數不夠前面補零

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

long long a[100005];
struct node///字典樹節點
{
    long long num;
    node* e[2];
    node()
    {
        memset(e,0,sizeof(e));
        num=-1;
    }
}*root =new node;

///將一個數轉化爲2進制字符串
void change(long long x,char p[])///要返回一個字符串,可以直接
{                    ///用字符串做參數,在函數裏面改變其值,返回的值也改變了
    int cnt=0;       ///char p[]和char *p效果一樣
    while(x)
    {
        if(x&1) p[cnt++]='1';
        else p[cnt++]='0';
        x/=2;
    }
    for(int i=cnt;i<=31;i++)
        p[i]='0';
    reverse(p,p+31);
}

void Insert(long long x)///插入函數
{
    char c[35]="\0";
    change(x,c);
    node *o=root;
    for(int i=0;c[i];i++)
    {
        if(!o->e[c[i]-'0'])  o->e[c[i]-'0']=new node;
        o=o->e[c[i]-'0'];
    }
    o->num=x;
}

long long query(long long x)///查詢函數
{
    char c[35]="\0";
    change(x,c);
    node *o=root;
    for(int i=0;c[i]&&o;i++)
    {
        int n=c[i]-'0';
        if(o->e[!n])  o=o->e[!n];///往與其相反的方向查詢
        else  o=o->e[n];		 ///往與其相同的方向查詢
    }
    return x^o->num;
}

int main()
{
    int n;cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        Insert(a[i]);
    }
    long long maxn=0;
    for(int i=1;i<=n;i++)  maxn=max(maxn,query(a[i]));
    cout<<maxn<<endl;
    return 0;
}

代碼2
其實不需要使用change()函數,直接使用位移運算符<<即可,簡化後的代碼

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

long long a[100005];
struct node
{
    long long num;
    node* e[2];
    node()
    {
        memset(e,0,sizeof(e));
        num=-1;
    }
}*root =new node;

void Insert(long long x,node *o=root)
{
    for(int i=30;i>=0;i--)
    {
        int bit=x>>i&1;///注意要&1,否則不是最後一位
        if(!o->e[bit])  o->e[bit]=new node;
        o=o->e[bit];
    }
    o->num=x;
}

long long query(long long x,node *o=root)
{
    for(int i=30;i>=0;i--)
    {
        int n=x>>i&1;
        if(o->e[!n])  o=o->e[!n];
        else  o=o->e[n];
    }
    return x^o->num;
}

int main()
{
    int n;cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        Insert(a[i]);
    }
    long long maxn=0;
    for(int i=1;i<=n;i++)  maxn=max(maxn,query(a[i]));
    cout<<maxn<<endl;
    return 0;
}

代碼3
使用數組實現
數組實現的解析可以參考:博客

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int trie[100000*31+10][2];///節點數組,最多100000*31個節點
int tot=0;///節點數

void Insert(int x)
{
    int e=0;///根節點
    for(int i=30;i>=0;i--)
    {
        int bit=x>>i&1;///獲取當前位的01值
        if(!trie[e][bit]) trie[e][bit]=++tot;///建立節點
        e=trie[e][bit];///向建立好的節點移動
    }
}

long query(int x)
{
    int e=0;
    long ans=0;///x和前面所有數異或的最大值
    for(int i=30;i>=0;i--)
    {
        int bit=x>>i&1;///獲取當前位的01值
        if(trie[e][!bit])///向着與當前位的值相反方向查詢
        {
            e=trie[e][!bit];///節點移動
            ans|=(1<<i);///當前節點和x的第i位異或位1,則ans第i位爲1
        }
        else e=trie[e][bit];
    }
    return ans;
}

int main()
{
    int n;cin>>n;
    long maxn=0;
    while(n--)
    {
        int x; cin>>x;
        Insert(x);///插入
        maxn=max(maxn,query(x));///可以直接查詢並更新最大值
    }
    cout<<maxn<<endl;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章