藍橋杯--算法訓練

《1》區間k大數查詢  

問題描述

給定一個序列,每次詢問序列中第l個數到第r個數中第K大的數是哪一個。node

輸入格式

第一行包含一個數n,表示序列長度。算法

第二行包含n個正整數,表示給定的序列。數組

第三個包含一個正整數m,表示詢問個數。學習

接下來m行,每行三個數l,r,K,表示詢問序列從左往右第l個數到第r個數中,從大往小第K大的數是哪一個。序列元素從1開始標號。測試

輸出格式
總共輸出m行,每行一個數,表示詢問的答案。
樣例輸入
5
1 2 3 4 5
2
1 5 2
2 3 2
樣例輸出
4
2
數據規模與約定

對於30%的數據,n,m<=100;spa

對於100%的數據,n,m<=1000;調試

保證k<=(r-l+1),序列中的數<=106code

複製代碼
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;

int compare(int a,int b)//降序排序
{
    return a>b;
}

int main()
{
    int n,m,i;
    scanf("%d",&n);
    int a[n+1],b[n+1];
    
    for(i=1;i<=n;i++)scanf("%d",&a[i]);
    
    int l,r,k,j;
    scanf("%d",&m);
    while(m--)
    {
        
        scanf("%d%d%d",&l,&r,&k);
        for(i=l,j=1;i<=r;i++,j++)b[j]=a[i];
        sort(b+1,b+(r-l+2),compare);
    //    for(i=1;i<=(r-l)+1;i++)printf("%d ",b[i]);
    //    printf("\n");
        printf("%d\n",b[k]);
    }
    return 0;
} 
複製代碼

 

《2》 最大最小公倍數

問題描述blog

已知一個正整數N,問從1~N-1中任選出三個數,他們的最小公倍數最大能夠爲多少。排序

輸入格式

輸入一個正整數N。

輸出格式

輸出一個整數,表示你找到的最小公倍數。

樣例輸入

9

樣例輸出

504

數據規模與約定

1 <= N <= 106

題目分析

  當n爲奇數時,答案必定是n*(n-1)*(n-2)。

  當n爲偶數時,答案多是(n-1)*(n-2)*(n-3),也多是n*a*b,其中a>=n-3。

參考程序

  

複製代碼
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<math.h>


//展轉相除法求最大公約數,最小公倍數
long long RoudDiv(long long  m,long long n)
{
    long long m1,n1;
    if( m >=n)
    {
        m1 = m;
        n1 = n;
    }
    else
    {
        m1 = n;
        n1 = m;
    }
    long long r = m1%n1;
    while( r!=0 )
    {
        m1 = n1;
        n1 = r;
        r = m1%n1;
    }
    //最大公約數n,求最小公倍數
    return m*n/n1;
} 

//求三個數的最小公倍數 
long long getMulMin(int a,int b,int c)
{
    return RoudDiv(RoudDiv(a,b),c);
}

//檢查min和max是否互素 
int IsShus( int min,int max)
{
    int i;
    
    if( min>max )
    {
        int temp = max;
        max = min;
        min = temp;
    }
    
    for( i=2; i<=min;i++ )
    {
        if( min%i==0 && max%i==0  )
        {
            break;
        }
    }
    
    if ( i>min )
    {
        return 1;
    }
    else
    {
        return 0;
    }    
}

long long FindMaxMul(int* data)
{
    int i,j,k;
    int resul[3];
    int n = data[0];
    
    if( n%2 != 0)
    {            
        return getMulMin( data[n],data[n-1],data[n-2]);
    }
    
    
    long long max = getMulMin( data[n-1],data[n-2],data[n-3]);
    long long a;
        
//    printf("data = %d %d %d\n",data[n-1],data[n-2],data[n-3]);    
//    printf("max=%I64d\n",max);    
        
    resul[0] = data[n-1];
    for( j=n-1; j>=n-3; j--)
    {    
        for( k=j-1 ; k>=1; k--)
        {
            if( IsShus(data[k],data[n]) && IsShus(data[k],data[j]) )
            {
                if ( (a = getMulMin( data[n],data[j],data[k]) )> max)
                {
                    resul[0] = data[n];
                    resul[1] = data[j];
                    resul[2] = data[k];
                    max = a;
    //                printf("data = %d %d %d\n",data[n],data[j],data[k]);
                }
                break;
            }
        }
    }
    
//    printf("%d %d %d\n",resul[0],resul[1],resul[2]);
//    printf("%d",a);
    return max;
}


int main()
{
    int n,i;
    scanf("%d",&n);
    int data[n+1];
    data[0] = n;
    for ( i=1; i<=n ; i++)
    {
        data[i] = i;
    }
    
    if ( n==1 || n==2)
    {
        printf("%d",n);
    }
    else
    {
    
//    printf("\n");
    clock_t start, finish;
    start = clock();
    long long a=FindMaxMul(data);
    printf("%I64d",a);
    finish = clock();
    double duration = (double)(finish - start);
//    printf( "\n%f 毫秒\n", duration );
    }
    
    return 0;
}
複製代碼

 

《3》 K好數

問題描述

若是一個天然數N的K進製表示中任意的相鄰的兩位都不是相鄰的數字,那麼咱們就說這個數是K好數。求L位K進制數中K好數的數目。例如K = 4,L = 2的時候,全部K好數爲十一、1三、20、2二、30、3一、33 共7個。因爲這個數目很大,請你輸出它對1000000007取模後的值。

輸入格式

輸入包含兩個正整數,K和L。

輸出格式

輸出一個整數,表示答案對1000000007取模後的值。

樣例輸入

4 2

樣例輸出

7

數據規模與約定

對於30%的數據,KL <= 106

對於50%的數據,K <= 16, L <= 10;

對於100%的數據,1 <= K,L <= 100。

題目分析

   此題用動態規劃求解。求L位K進制數中K好數的數目,咱們能夠先求出L-1位K好數,L-2位K好數......1位K好數。設F[i][j]表示i位以數字j結尾的K好數數目,1<=i<=L,0<=j<K

既i位的以j結尾的K好數,等於i-1位的且最後一位與j不相鄰的K好數個數的和。

心得體會

在數字大於計算機能夠表示的範圍時,有可能迭代過程就是錯的了。而對於數據很大的時候,單步調試的方法彷佛是行不通的,因此這個是有就應該在適當的位置輸出迭代結果,觀察輸出是否與預想的一致,而後慢慢將範圍縮小,縮小到能夠進行單步調試的範圍。固然,若是發現了錯誤的話就不必進行單步調試了。
在這個算法中,對最後答案取餘,其實在迭代過程當中f[i][j]早已越界,因此應該在迭代過程當中就只存餘數。在調試過程當中,總以錯誤的答案爲參照去懷疑正確的答案,這也是一個敗筆。應該堅持調試的時候輸出迭代過程,這樣才能很好的判斷答案的正確與否。

(a+b) mod c = ((a mod c) + (b mod c)) mod c

數組最大能夠到data[100000]

參考代碼

複製代碼
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
# define MOD 1000000007

long long f[101][100000];

//求K好數 
long long KGoodNum(int l,int k)
{
    int i,j,p;
    
    //以數字i結尾的1位K好數的數目 
    for( i=0; i<k; i++)
    {
        f[1][i] = 1;
    }
    
    for ( i=2; i<=l; i++ )//求2位開始到l位的K好數 
    {
        for( j=0; j<k; j++ )//i位的最後一位數j 
        {
            f[i][j] = 0;
            for ( p=0; p<k; p++)//i-1位的最後一位數p 
            {
                if( p+1!=j && p-1!=j )
                {
                    f[i][j] =(f[i][j] + f[i-1][p])%MOD;//只用存儲餘數便可 
                    //    printf("i= %d; j=%d; f[i][j]= %I64d\n",i,j,f[i][j]);
                }
                
            }
        }        
    }
    
    //l位的K好數總數 
    long long sum = 0;
    for ( i=1; i<k; i++)
    {
        sum += f[l][i];
    }
    
    return sum%MOD;
}


int main()
{
    int k,l;
    clock_t start,finish;
    scanf("%d%d",&k,&l);
    start = clock();
    
    long long sum = 0;
    
    sum = KGoodNum(l,k);

    printf("%I64d",sum);
    
    finish = clock();
    double t = (double)(finish - start);
//    printf("\ntime = %f",t);
    
    return 0;
}
複製代碼

 

《4》 結點選擇

問題描述

有一棵 n 個節點的樹,樹上每一個節點都有一個正整數權值。若是一個點被選擇了,那麼在樹上和它相鄰的點都不能被選擇。求選出的點的權值和最大是多少?

輸入格式

第一行包含一個整數 n 。

接下來的一行包含 n 個正整數,第 i 個正整數表明點 i 的權值。

接下來一共 n-1 行,每行描述樹上的一條邊。

輸出格式

輸出一個整數,表明選出的點的權值和的最大值。

樣例輸入

5
1 2 3 4 5
1 2
1 3
2 4
2 5

樣例輸出

12

樣例說明

選擇三、四、5號點,權值和爲 3+4+5 = 12 。

數據規模與約定

對於20%的數據, n <= 20。

對於50%的數據, n <= 1000。

對於100%的數據, n <= 100000。

權值均爲不超過1000的正整數。

題目分析

  本題應該是用樹形動態規劃是比較合適的。其中設F[v][1]表示選擇節點i的最大權值,F[v][0]表示不選擇節點i的最大權值,pow[i]表示節點i的權值,再設與v相鄰的節點u爲v的孩子節點,則可有

F[v][1]=F[u][0]+pow[v],F[v][0]=max{F[u][0],F[u][1]};若v有多個孩子節點,u1,u2,....uk,可有:

根據公式進行樹形動歸就能夠將問題解決。可是這裏要注意的是,樹的存儲結構選擇至關重要,若是用二維數組存儲樹,那麼空間最大達到100000*100000,但是纔有100000-1條邊,因此對空間的浪費是客觀的。而此題涉及到有關孩子的操做比較多,綜合狀況,採用了孩子鏈表表示法存儲樹的結構。

參考代碼

複製代碼
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
#define M 100100
using namespace std;

typedef struct Node
{
    int vex;
    Node* next;
}Child;

Child* head[M];
int f[M][2],pow[M],visit[M];

//添加邊 
void addADJ(int u,int v)
{
    Child *p,*q;
    p=(Child*)malloc(sizeof(Child));
    p->vex=v;
    p->next=head[u];
    head[u]=p;
    
    q=(Child*)malloc(sizeof(Child));
    q->vex=u;
    q->next=head[v];
    head[v]=q;
}
    
//動態規劃 
void GetResul(int v)
{

    visit[v]=1;
    Child *p;
    for(p=head[v];p!=NULL;p=p->next)
    {
        if(visit[p->vex]==0)
        {
            GetResul(p->vex);
            f[v][1] = f[v][1]+f[p->vex][0];
            f[v][0]+=max(f[p->vex][0],f[p->vex][1]);
        }
    }    
    f[v][1]+=pow[v];
}


int main()
{
    int i,j,u,v,n;
    
    memset(head,NULL,sizeof(head));
    memset(f,0,sizeof(f));
    memset(visit,0,sizeof(visit));
    
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    {
        scanf("%d",&pow[i]);
    }
    for(i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        addADJ(u,v);
    }
    
    GetResul(1);
    printf("%d\n",max(f[1][0],f[1][1]));
    
    return 0;
}
複製代碼

 

《5》 最短路 

問題描述

給定一個n個頂點,m條邊的有向圖(其中某些邊權可能爲負,但保證沒有負環)。請你計算從1號點到其餘點的最短路(頂點從1到n編號)。

輸入格式

第一行兩個整數n, m。

接下來的m行,每行有三個整數u, v, l,表示u到v有一條長度爲l的邊。

輸出格式

共n-1行,第i行表示1號點到i+1號點的最短路。

樣例輸入

3 3
1 2 -1
2 3 -1
3 1 2

樣例輸出

-1
-2

數據規模與約定

對於10%的數據,n = 2,m = 2。

對於30%的數據,n <= 5,m <= 10。

對於100%的數據,1 <= n <= 20000,1 <= m <= 200000,-10000 <= l <= 10000,保證從任意頂點都能到達其餘全部頂點。

題目分析

  起初剛看這道題目的時候,第一感受就想到了鄰接矩陣加Dijkstra求最短路徑。可是程序提交後,竟然是錯誤的,結果發現內存佔用率很大。再回頭看題目,發現這個圖相對是比較稀疏的圖,

因此用鄰接數組存儲會浪費不少空間。在這樣的狀況下,應該選擇鄰接表存儲圖是比較合適的。通過改進後,提交代碼經過。因此再看到題目後應該仔細分析題目的數據規模,選擇合適的存儲結構,直接影響到截正確的解答問題。

參考代碼

複製代碼
/*
    測試已經過
    用鄰接表實現 
*/
#include<stdio.h>
#include<algorithm>
#include<queue>
#include<string.h>
using namespace std;
const int maxsize=200000;
const int inf = 1000000;

typedef struct XNode
{
    int pow;
    int adjvex;
    struct XNode* next;
}Node;

Node* head[maxsize];
int visit[maxsize],dist[maxsize];


//添加邊 
void AddAdj(int u,int v,int l)
{
    Node *p;
    p = (Node*)malloc(sizeof(Node));
    p->pow=l;
    p->adjvex=v;
    p->next=head[u];
    head[u]=p;
} 

//求最短路徑 
void shortpath(int n)
{
    int i,j,u,v,w;
    queue<int> Q;
    Node *p;
    for(i=1;i<=n;i++)
    {
        visit[i]=0;
        dist[i]=inf;
    }
    
    //第一個節點入隊列 
    Q.push(1);
    dist[1]=0;
    visit[1]=1;
    
    while(!Q.empty())//檢查全部節點 
    {
        u=Q.front();
        Q.pop();
        for(p=head[u];p!=NULL;p=p->next)
        {
            v=p->adjvex;
            w=p->pow;
            if(dist[u]+w<dist[v])//經過u到v會比原路徑到達v要近 
            {
                dist[v]=dist[u]+w;
                if(!visit[v])
                {
                    visit[v]=1;
                    Q.push(v);
                }
            }
        }
    }
    
    
    
}


int main()
{
    int n,m,u,v,w;
    
    scanf("%d%d",&n,&m);
    int i;
    memset(head,NULL,sizeof(head));
    for(i=1;i<=m;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        AddAdj(u,v,w);
    }
    
    shortpath(n);
    for(i=2;i<=n;i++)
        printf("%d\n",dist[i]);
    
    return 0;
}
複製代碼

 

《6》安慰奶牛

問題描述

Farmer John變得很是懶,他不想再繼續維護供奶牛之間供通行的道路。道路被用來鏈接N個牧場,牧場被連續地編號爲1到N。每個牧場都是一個奶牛的家。FJ計劃除去P條道路中儘量多的道路,可是還要保持牧場之間 的連通性。你首先要決定那些道路是須要保留的N-1條道路。第j條雙向道路鏈接了牧場Sj和Ej(1 <= Sj <= N; 1 <= Ej <= N; Sj != Ej),並且走完它須要Lj的時間。沒有兩個牧場是被一條以上的道路所鏈接。奶牛們很是傷心,由於她們的交通系統被削減了。你須要到每個奶牛的住處去安慰她們。每次你到達第i個牧場的時候(即便你已經到過),你必須花去Ci的時間和奶牛交談。你每一個晚上都會在同一個牧場(這是供你選擇的)過夜,直到奶牛們都從悲傷中緩過神來。在早上 起來和晚上回去睡覺的時候,你都須要和在你睡覺的牧場的奶牛交談一次。這樣你才能完成你的 交談任務。假設Farmer John採納了你的建議,請計算出使全部奶牛都被安慰的最少時間。

輸入格式

第1行包含兩個整數N和P。

接下來N行,每行包含一個整數Ci

接下來P行,每行包含三個整數Sj, Ej和Lj

輸出格式

輸出一個整數, 所須要的總時間(包含和在你所在的牧場的奶牛的兩次談話時間)。

樣例輸入

5 6
10
10
20
6
30
1 2 5
2 3 5
2 4 12
3 4 17
2 5 15
3 5 6

樣例輸出

178

數據規模與約定

5 <= N <= 10000,N-1 <= P <= 100000,0 <= Lj <= 1000,1 <= Ci <= 1,000。

題目分析

  由數據規模能夠看出,圖的邊相對比較稀疏,因此用Kruskal算法求最小生成樹。既,每次從剩餘的邊中選擇一條權值最短的邊,而且加邊之後不會構成環,直到全部的頂點都連通爲止。由題可知道,源點既爲C值最小的點,而每條道路都會走兩次,因此道路權值應該爲2*L+Cs+Ce;

參看代碼

複製代碼
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
#define M 100000 
using namespace std;

//邊節點 
typedef struct node
{
    int s,e,l;
}Edeg;

Edeg edg[M];
int c[M],pre[M];

int compare(Edeg e1,Edeg e2)//升序 
{
    return e1.l<e2.l;
}

int findRoot(int n)//查找同集合的表明元素 
{
    if(pre[n]==n)return n;
    int t= findRoot(pre[n]);
    pre[n]=t;
    return t;
}

int main()
{
//    FILE *f;
//    f=fopen("安慰奶牛data.txt","r+");
    int i,j,k,n,p,minn=M;
//    fscanf(f,"%d%d",&n,&p);
    scanf("%d%d",&n,&p);
    //輸入節點權值 
    for(i=1;i<=n;i++)
    {
    //    fscanf(f,"%d",&c[i]);
        scanf("%d",&c[i]);
        if(c[i]<minn)minn=c[i];
        pre[i]=i;
    }
    //輸入邊 
    for(i=1;i<=p;i++)
    {
        //fscanf(f,"%d%d%d",&edg[i].s,&edg[i].e,&edg[i].l);
        scanf("%d%d%d",&edg[i].s,&edg[i].e,&edg[i].l);
        edg[i].l =2*edg[i].l+c[edg[i].s]+c[edg[i].e];//邊的權值 
    }
    
    int r1,r2,sum=0;
    sort(edg+1,edg+p+1,compare);
    for(i=1;i<=p;i++)
    {
        r1=findRoot(edg[i].s);
        r2=findRoot(edg[i].e);
        if(r1!=r2)
        {
            sum+=edg[i].l;
            pre[r2]=r1;
        }
    }
    
    sum+=minn;//晚上回來還要跟源點奶牛進行一次交談
    printf("%d\n",sum);
    
    return 0;
} 
複製代碼

 

《7》逆序對

問題描述

Alice是一個讓人很是愉躍的人!他老是去學習一些他不懂的問題,而後再想出許多稀奇古怪的題目。這幾天,Alice又沉浸在逆序對的快樂當中,他已近學會了如何求逆序對對數,動態維護逆序對對數等等題目,他認爲把這些題讓你作簡直是太沒追求了,因而,通過一天的思考和完善,Alice終於拿出了一道他認爲差很少的題目:

有一顆2n-1個節點的二叉樹,它有剛好n個葉子節點,每一個節點上寫了一個整數。若是將這棵樹的全部葉子節點上的數從左到右寫下來,便獲得一個序列a[1]…a[n]。如今想讓這個序列中的逆序對數量最少,但惟一的操做就是選樹上一個非葉子節點,將它的左右兩顆子樹交換。他能夠作任意屢次這個操做。求在最優方案下,該序列的逆序對數最少有多少。

Alice本身已近想出了題目的正解,他打算拿來和你分享,他要求你在最短的時間內完成。

輸入格式

第一行一個整數n。

下面每行,一個數x。

若是x=0,表示這個節點非葉子節點,遞歸地向下讀入其左孩子和右孩子的信息,若是x≠0,表示這個節點是葉子節點,權值爲x。

輸出格式

輸出一個整數,表示最少有多少逆序對。

樣例輸入

3
0
0
3
1
2

樣例輸出

1

數據規模與約定

對於20%的數據,n <= 5000。

對於100%的數據,1 <= n <= 200000,0 <= a[i]<2^31。

 

《8》 操做格子

問題描述

有n個格子,從左到右放成一排,編號爲1-n。

共有m次操做,有3種操做類型:

1.修改一個格子的權值,

2.求連續一段格子權值和,

3.求連續一段格子的最大值。

對於每一個二、3操做輸出你所求出的結果。

輸入格式

第一行2個整數n,m。

接下來一行n個整數表示n個格子的初始權值。

接下來m行,每行3個整數p,x,y,p表示操做類型,p=1時表示修改格子x的權值爲y,p=2時表示求區間[x,y]內格子權值和,p=3時表示求區間[x,y]內格子最大的權值。

輸出格式

有若干行,行數等於p=2或3的操做總數。

每行1個整數,對應了每一個p=2或3操做的結果。

樣例輸入

4 3
1 2 3 4
2 1 3
1 4 3
3 1 4

樣例輸出

6
3

數據規模與約定

對於20%的數據n <= 100,m <= 200。

對於50%的數據n <= 5000,m <= 5000。

對於100%的數據1 <= n <= 100000,m <= 100000,0 <= 格子權值 <= 10000。

題目分析  

  我第一次是用數組存樹的信息,結果沒經過,可是也沒發現是什麼問題。後來就採用二叉鏈表來存儲線段樹,結果經過了。雖然沒有發現第一個緣由出錯在哪,不過用鏈表動態存儲樹的話仍是相對比較方便的,用遞歸的思想對樹進行操做也是很方便快捷的。

參考代碼

複製代碼
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>

using namespace std;

typedef struct node
{
    int sum,max,a,b;
    struct node *left,*right;
}TNode;



TNode* Buld(int a,int b,TNode* r)
{
    r=(TNode*)malloc(sizeof(TNode));
    r->a=a;r->b=b;
    r->sum=0;r->max=0;
    if(a==b)
    {
        r->left=r->right=NULL;
    }
    else
    {    
        r->left=Buld(a,(a+b)/2,r->left);
        r->right=Buld((a+b)/2+1,b,r->right);
    }
    
    return r;
}

void Insert(TNode *T,int x,int y)
{
    if(T->a==x&&T->b==x)
    {
        T->sum=y;
        T->max=y;
    }
    else
    {
        if(x<=(T->a+T->b)/2)
        {
            Insert(T->left,x,y);
        }
        else
        {
            Insert(T->right,x,y);
        }
        
        T->sum=T->left->sum+T->right->sum;
        T->max=T->left->max>T->right->max?T->left->max:T->right->max;
    }
}

int GetSum(TNode *T,int x,int y)
{
    int sum=0;
    if(x<=T->a&&T->b<=y)
        sum+=T->sum;
    else
    {
        int mid=(T->a+T->b)/2;
        if(y<=mid)
            sum+=GetSum(T->left,x,y);
        else if(x>mid)
            sum+=GetSum(T->right,x,y);
        else
            sum +=(GetSum(T->left,x,y)+GetSum(T->right,x,y));
             
    }
    
    return sum;
}

int GetMax(TNode *T,int x,int y)
{
    int max;
    if(x<=T->a&&T->b<=y)
        max=T->max;
    else
    {
        int mid = (T->a+T->b)/2;
        if(y<=mid)
            max=GetMax(T->left,x,y);
        else if(x>mid)
            max=GetMax(T->right,x,y);
        else
        {
            int a1=GetMax(T->left,x,y);
            int a2=GetMax(T->right,x,y);
            max=a1>a2?a1:a2;
        }
            
    }
    
    return max;
}

void display(TNode *p)
{
    if(p==NULL)return;
    printf("a=%d b=%d sum=%d max=%d\n",p->a,p->b,p->sum,p->max);
    display(p->left);
    display(p->right);
}

int main()
{
    TNode *tree=NULL;
    int i,n,m,p,x,y,a,sum,max;
    scanf("%d%d",&n,&m);
    
    tree=Buld(1,n,tree);
    for(i=1;i<=n;i++)
    {
        scanf("%d",&a);
        Insert(tree,i,a);
    }
    
    while(m--)
    {
    //    display(tree);
        scanf("%d%d%d",&p,&x,&y);
        if(p==1)
            Insert(tree,x,y);
        else if(p==2)
        {    
            sum=GetSum(tree,x,y);
            printf("%d\n",sum); 
        }
        else if(p==3)
        {
            max=GetMax(tree,x,y);
            printf("%d\n",max);
        } 
        
    }
    
    return 0;
}
相關文章
相關標籤/搜索