Acwing(二)

第一節(jié)
1燎字、鏈表與鄰接表
2、棧與隊(duì)列
3阿宅、Kmp

一候衍、鏈表

1、單鏈表 : 鄰接表
鄰接表作用 存儲(chǔ)圖和樹
2洒放、雙鏈表 用來優(yōu)化某些問題

e[N] 某個(gè)點(diǎn)的值
ne[N] 某個(gè)節(jié)點(diǎn)的next指針
他們用下標(biāo)關(guān)聯(lián)起來

最后一個(gè)元素的next指針指向空集 ne[n-1]=-1
單鏈表只能找到一個(gè)節(jié)點(diǎn)的下一個(gè)數(shù)蛉鹿,無(wú)法找到上一個(gè)數(shù)
注意:下標(biāo)是從0開始的,0是第一個(gè)插入的點(diǎn)
第k個(gè)插入的點(diǎn)的下標(biāo)是k-1

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
//head 表示頭節(jié)點(diǎn)的next指針 
//e[i]表示節(jié)點(diǎn)i的值
//ne[i]表示節(jié)點(diǎn)i的next指針
//idx是指針 表示當(dāng)前已經(jīng)用到了哪個(gè)點(diǎn)的空間 
int head,e[N],ne[N],idx;
void init(){
   head=-1;
   idx=0;
}
//將x插到頭節(jié)點(diǎn)的位置 
void add_to_head(int x){
   e[idx]=x;
   ne[idx]=head;
   head=idx;
   idx++;
}
//將x插入到下標(biāo)是k的點(diǎn)后面 
void add(int k,int x){
   e[idx]=x;
   ne[idx]=ne[k];
   ne[k]=idx;
   idx++;
} 
//將下標(biāo)是k的后一個(gè)點(diǎn)刪掉 
void remove(int k){
   ne[k]=ne[ne[k]]; 
} 

int main()
{
   int m;
   cin>>m;
   init(); 
   while(m--){
   char c;
   int k,x;
   cin>>c;
   if(c=='H'){
       cin>>x;
       add_to_head(x);
   }
   else if(c=='D'){
       cin>>k;
       if(k==0)
           head=ne[head]; //刪除頭節(jié)點(diǎn) 
   else    remove(k-1);
   }
   else if(c=='I'){
       cin>>k>>x;
       add(k-1,x);
   }
   } 
   for(int i=head;i!=-1;i=ne[i])
       cout<<e[i]<<" ";
   cout<<endl;
   return 0;
}

二往湿、雙鏈表

l[N] 指向 N 前面的節(jié)點(diǎn)
r[N] 指向 N 后面的節(jié)點(diǎn)
e[N] 表示這個(gè)點(diǎn)的值
讓下標(biāo)是0 的點(diǎn)為head
下標(biāo)為1的點(diǎn)為最后一個(gè)點(diǎn)

#include<bits/stdc++.h>
using namespace std;
int m;
const int N=1e5+10;
int e[N],l[N],r[N],idx;
void init(){
    r[0]=1;//0表示左端點(diǎn)妖异,l表示右端點(diǎn) 
    l[1]=0;
    idx=2;
}
//在下標(biāo)為k的右邊插入一個(gè)點(diǎn)x
//若要在下標(biāo)為k的左邊插入一個(gè)點(diǎn)x
//只需要調(diào)用 l[k] c就可以 
void add(int k,int c){
    
    e[idx]=c;
    r[idx]=r[k];
    l[idx]=k;
    l[r[k]]=idx;
    r[k]=idx;
    idx++;
} 
//刪除第k個(gè)點(diǎn) 
void remove(int k){
    r[l[k]]=r[k];
    l[r[k]]=l[k]; 
}  

int main()
{
    cin>>m;
    init();
    while(m--)
    {
        string op;
        cin>>op;
        if(op=="L"){
            int x;
            cin>>x;
            add(0,x); //在最左側(cè)的右側(cè)插入一個(gè)點(diǎn) 
        }            //左端點(diǎn)是0 
        else if(op=="R"){
            int x;
            cin>>x;
            add(l[1],x);//在最右側(cè)的左側(cè) 
        }               //最右側(cè)端點(diǎn)的是1 插入l[1]后面 
        else if(op=="D"){
            int k;
            cin>>k;
            remove(k+1);//idx從2開始 所以是k+1 
        }
        else if(op=="IL"){
            int k,x;
            cin>>k>>x;
            add(l[k+1],x);//k+1
        }
        else if(op=="IR"){
            int k,x;
            cin>>k>>x;
            add(k+1,x);
        }
    }
    for(int i=r[0];i!=1;i=r[i]){
        cout<<e[i]<<" ";//左端點(diǎn)不輸出 
    return 0;//從左端點(diǎn)的右側(cè)輸出
            //i!=-1就沒遍歷到右端點(diǎn) 
}           //i=r[i] i每次變?yōu)橛覀?cè)的點(diǎn) 

三领追、鄰接表

n個(gè)單鏈表
把每個(gè)表的所有鄰邊都存起來

四他膳、模擬棧

int stk[N],tt;
stk[++tt]=x;//插入
tt--;   //彈出

if(tt>0) not empty
else empty
stk[tt];//棧頂 

//int stk[N],tt;
//stk[++tt]=x;//插入
//tt--; //彈出

//if(tt>0) not empty
//else empty
//stk[tt];//棧頂 

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int main(){
    int stack[N],tt;
    int m;
    cin>>m;
    while(m--){
        string op;
        cin>>op;
        int k,x;
        if(op=="push"){
            cin>>x;
            stack[++tt]=x; //插入x 
        }
        else if(op=="pop"){
            tt--;//彈出棧頂元素 
        }
        else if(op=="query"){
            cout<<stack[tt]<<endl;//tt是棧頂 
        }
        else if(op=="empty"){
            if(tt>0)//tt大于0 則stack不為空 
                cout<<"NO"<<endl;
                else cout<<"YES"<<endl;
        }
    } 
    
    
    return 0;
}

五、模擬隊(duì)列

////在隊(duì)尾插入元素绒窑,在隊(duì)頭彈出元素
//int q[N],hh,tt=-1;
////插入
//q[++tt]=x;
////彈出
//h++;
////判斷是否為空 
//if(hh<=tt)not empty
//else empty
////去除隊(duì)頭元素
//q[hh]; 隊(duì)頭 
//q[tt];隊(duì)尾 


#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int q[N],hh,tt=-1;
int main(){
    int m;
    cin>>m;
    string op;
    while(m--){
        cin>>op;
        int x,k;
        if(op=="push"){
            cin>>x;
            q[++tt]=x;  //在隊(duì)尾加入一個(gè)元素 
        }
        else if(op=="pop"){
    
            hh++;  //隊(duì)頭++ 就是彈出隊(duì)頭 
        }
        else if(op=="query"){
            cout<<q[hh]<<endl; //輸出隊(duì)頭 
        }
        else if(op=="empty"){
            if(hh<=tt)    //如果隊(duì)頭是小于等于隊(duì)尾的 就非空 
            cout<<"NO"<<endl;
            else cout<<"YES"<<endl;//否則為空 
        }
    }
    return 0;
}

六.單調(diào)棧

對(duì)暴力`進(jìn)行優(yōu)化
題型 找到左邊或者右邊,第一個(gè)比它大或者比他小的數(shù)

#include<iostream>
using namespace std;
const int N=1e5+10;
int a[N],s[N]; //s為棧 
int tt=0;
int main()
{
   int n;
   cin>>n;
   for(int i=1;i<=n;i++)
   {
       cin>>a[i];  
       while(tt&&s[tt]>=a[i])tt--;//tt代表?xiàng)2粸榭?
       if(tt)cout<<s[tt]<<" "; //s[tt]>=a[i]當(dāng)前棧頂元素大于當(dāng)前元素則把他彈出 
       else cout<<-1<<' '; //操作后如果棧不為空則棧頂元素就是第一個(gè)小于當(dāng)前元素的元素
        
       s[++tt]=a[i];//再把當(dāng)前元素入棧 
   }
   return 0;
}

七. 單調(diào)隊(duì)列

同樣是對(duì)暴力進(jìn)行優(yōu)化
應(yīng)用: 求滑動(dòng)窗口中的最大值和最小值

棧和隊(duì)列就是刪除沒有用的元素,維護(hù)一個(gè)單調(diào)性

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int a[N],q[N];
int tt=-1,hh;

int main()
{
   ios::sync_with_stdio(false);
   cin.tie(0);cout.tie(0);
   int n,k;
   cin>>n>>k;
   for(int i=1;i<=n;i++){
       cin>>a[i];
   }
   for(int i=1;i<=n;i++){
       if(hh<=tt&&i-k+1>q[hh])hh++;
   //隊(duì)列不為空,如果起點(diǎn)>q[hh] 隊(duì)列中存的是下標(biāo)
   //則需要將窗口向后移動(dòng)一個(gè) h++; 
       while(hh<=tt&&a[q[tt]]>=a[i])tt--;
//隊(duì)列不為孔二 且隊(duì)尾下標(biāo)對(duì)應(yīng)元素大于a[i]則彈出 
       q[++tt]=i;//操作結(jié)束把新下標(biāo)入隊(duì) 
       if(i-k+1>=1)cout<<a[q[hh]]<<" ";        
       //從第一個(gè)窗口開始輸出 隊(duì)頭就是最小值 
   }
   cout<<endl;
   
       tt=-1,hh=0;
       for(int i=1;i<=n;i++){
       if(hh<=tt&&i-k+1>q[hh])hh++;
   //隊(duì)列不為空,如果起點(diǎn)>q[hh] 隊(duì)列中存的是下標(biāo)
   //則需要將窗口向后移動(dòng)一個(gè) h++; 
       while(hh<=tt&&a[q[tt]]<=a[i])tt--;
//隊(duì)列不為孔二 且隊(duì)尾下標(biāo)對(duì)應(yīng)元素大于a[i]則彈出 
       q[++tt]=i;//操作結(jié)束把新下標(biāo)入隊(duì) 
       //注意先把這個(gè)樹加進(jìn)去再輸出 
       
       if(i-k+1>=1)cout<<a[q[hh]]<<" ";        
       //從第一個(gè)窗口開始輸出 隊(duì)頭就是最小值 
   }
   cout<<endl;
   
   return 0;
}

KMP 算法

S[N] P[M] S長(zhǎng)串 P 短串

尋找原串中包含子串的數(shù)目
next[N];
next[N] 只和字串有關(guān) 預(yù)處理子串
以某一點(diǎn)為中點(diǎn)的后綴和前綴 最長(zhǎng)相等的長(zhǎng)度是多少


1 j i

p[1~j] = p[i-j+1~i];

*/

//7. KMP
//求Next數(shù)組:
// s[]是模式串棕孙,p[]是模板串, n是s的長(zhǎng)度,m是p的長(zhǎng)度
//for (int i = 2, j = 0; i <= m; i++)
//{
//  while (j && p[i] != p[j + 1]) j = ne[j];
//  if (p[i] == p[j + 1]) j++;
//  ne[i] = j;
//}

// 匹配
//for (int i = 1, j = 0; i <= n; i++)
//{
//  while (j && s[i] != p[j + 1]) j = ne[j];
//  if (s[i] == p[j + 1]) j++;
//  if (j == m)
//  {
//      j = ne[j];
//       匹配成功后的邏輯
//  }
//}

#include<bits/stdc++.h>
using namespace std;
int n,m;
const int N=10010,M=100010;
char p[N],s[M];
int ne[N];
int ans;
int main()
{
    cin>>n>>p+1>>m>>s+1;
    for(int i=2;i<=n;i++){
        while(j&&p[i]!=p[j+1])
            j=ne[j];
        if(p[i]==p[j+1])
            j++;
        ne[i]=j;
    }
    for(int i=1;i<=n;i++){
        while(j&&s[i]!=p[j+1])
            j=ne[j];
        if(p[i]==p[j+1])
            j++;
        if(j==n){
            cout<<i-n+1-1<<endl;
            j=ne[j];
        }
    }
    

    return 0;
}

trie 字典樹

在一個(gè)集合中快速查找和存儲(chǔ)一些字符串

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
//son[N][27] 表示一共有N 個(gè)節(jié)點(diǎn) 每個(gè)節(jié)點(diǎn)最多
//有 26 個(gè)兒子 所以二維的值是27
//cnt[N] 表示以某個(gè)字母為結(jié)尾的單詞數(shù)量
//idx 表示當(dāng)前創(chuàng)建的空間
int son[N][27],cnt[N],idx;
int n;
string str;
void Insert() {
    int len = str.size();
    int p = 0;
    for (int i = 0; i < str.size(); i++) {
        int u = str[i] - 'a';
        //如果沒有這個(gè)字母就創(chuàng)建一個(gè)
        if (!son[p][u])son[p][u]=++idx;
        //創(chuàng)建后改變p 的值 增加節(jié)點(diǎn)數(shù)
        p = son[p][u];
    }
    //單詞全部存儲(chǔ)完畢 則將cnt++
    cnt[p]++;
}
int Que() {
    int len = str.size();
    int p = 0;
    for (int i = 0; i < str.size(); i++) {
        int u = str[i] - 'a';
        //如果找到某個(gè)字母時(shí)發(fā)現(xiàn)沒有 則return0
        if (!son[p][u])return 0;
        //將p變成他的兒子

        else p = son[p][u];
    }
    return cnt[p];//返回這個(gè)單詞的數(shù)量

}
int main()
{
    cin >> n;
    while (n--) {
        char c;
        cin >> c>>str;
        if (c == 'I') {
            Insert();
        }
        else if (c == 'Q') {
            int ans=Que();
            cout << ans << endl;
        }

    }
    system("PAUSE");
    return 0;
}

最大異或?qū)?/h3>

將一個(gè)數(shù)的二進(jìn)制逐個(gè)插入字典樹
從最高位開始找 每次找這個(gè)數(shù)相反的支路
如果沒有相反的支路就走本支路

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
const int M=31e5+10;
int son[M][2],a[N],idx;
int n,ans=-0x3f3f3f3f;
void Insert(int x)
{
    int p = 0;
    for (int i = 30; i >=0; i--)
    {
        if (!son[p][x >> i & 1]) {
            //x>>i&1 分離出一個(gè)數(shù)的每個(gè)二進(jìn)制位
            son[p][x >> i & 1] = ++idx;
            //如果沒有則開建立一個(gè)兒子節(jié)點(diǎn)
        }
        p = son[p][x >> i & 1];
        //如果有則p變成他的兒子節(jié)點(diǎn)
    }
}
int Search(int x)
{
    int p = 0;
    long long res=0;
    for (int i = 30; i >= 0; i--) {

        if (son[p][!(x>>i&1)]) {
            //如果有與他相反的那個(gè)支路走這條
            p = son[p][!(x>>i&1)];
            //結(jié)果加上 1左移動(dòng)i位
            res += 1 << i;
        }
        else p = son[p][x>>i&1];
        //如果沒有相反的則走這條相同的路
        //異或后為0 res 不變
    }
    return res;

}
int main()
{
    /*int a = 1, b = 2,c;
    c = a ^ b;
    cout << c << endl;*/
    //cout << M << endl;

    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i];

    for (int i = 1; i <= n; i++)
    {
        Insert(a[i]);//插入字典樹
    }
    for (int i = 1; i <= n; i++)
    {
        ans=max(ans,Search(a[i]));
        //搜索每個(gè)元素異或最大的支路值
    }
    cout << ans << endl;
    system("PAUSE");
    return 0;
}

并查集

1些膨、將兩個(gè)集合合并
2散罕、詢問兩個(gè)元素是否在一個(gè)集合當(dāng)中
基本原理:
用樹的形式來維護(hù)一個(gè)集合。
每個(gè)樹的根節(jié)點(diǎn)的編號(hào)為這個(gè)集合的編號(hào)
每一個(gè)節(jié)點(diǎn)都存儲(chǔ)一下其父親節(jié)點(diǎn)是誰(shuí)P[x]表示x的父節(jié)點(diǎn)
問題1:如何判斷樹根傀蓉? 若P[X]=x 則x是樹根
問題2:如何求x的集合編號(hào) while(p[x]!=x)x=p[x];
問題3:如何合并兩個(gè)集合:若px是x集合的編號(hào)欧漱,py是y集合的編號(hào)
則 p[x]=y 把x集合合并到y(tǒng)集合當(dāng)中

并查集的優(yōu)化 : 路徑壓縮
遍歷后 將所有的兒子節(jié)點(diǎn)都指向根節(jié)點(diǎn)

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int p[N], idx;
int n, m;
int find(int x) {

    if (p[x] != x) //如果還沒找到根節(jié)點(diǎn) 則繼續(xù)往上找
        p[x]=find(p[x]);//找到后賦值給p[x]并且返回
                        // 注意找到后·賦值給p[x] 順便做了路勁壓縮
    //for (int i = 1; i <= n; i++)
    //  cout << p[i] << " ";
    //cout << endl;
    return p[x];
}
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        p[i] = i; //起初的時(shí)候每個(gè)數(shù)字是一個(gè)集合
                //初始化他們的根節(jié)點(diǎn)為本身
    }
    while (m--)
    {
        char c;
        int a, b;
        cin >> c >> a >> b;
        if (c == 'M') {
            p[find(a)] = find(b); //將a 直接并入b中
        } 
        else if (c == 'Q') {
            if (find(a) == find(b))//如果a b的根節(jié)點(diǎn)相同則屬于一個(gè)集合
                cout << "Yes" << endl;
            else cout << "No" << endl;
        }

    }
    //system("PAUSE");
    return 0;
}

連通塊中點(diǎn)的數(shù)量 837

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int p[N], cnt[N];
int find(int x) {
    if (p[x] != x)
        p[x] = find(p[x]);
    return p[x];
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        p[i] = i;
        cnt[i]++;
    }
    while (m--) {
        string op;
        int a, b;
        cin >> op;
        if (op =="C") {
            cin >> a >> b;
            if (find(a) != find(b))
            {
                int num = cnt[find(a)];

                p[find(a)] = find(b);
                cnt[find(b)] += num;
            }
        }
        else if (op == "Q1") {
            cin >> a >> b;
            if (find(a) == find(b))
                cout << "Yes" << endl;
            else cout << "No"<<endl;
        }
        else if (op == "Q2") {
            cin >> a;
            cout << cnt[find(a)] << endl;
        }


    }

    return 0;
}

費(fèi)解的開關(guān)

1、 按的順序可以任意
2葬燎、 按偶數(shù)次是不變的所以每個(gè)格子最多按一次按兩次沒用
3误甚、從第一行開始 第一行操作完不改變 則 第二行被第一行唯一確定

核心:每一行開關(guān)的操作完全被上一行燈的亮滅狀態(tài)所決定

問題
1缚甩;如何枚舉第一行的操作
位運(yùn)算二進(jìn)制枚舉
2^5=0~31
00000 00001
....~11111 16+8+4+2+1=31

for(int i=0;i<32;i++)
{
看第k位是否需要操作
i >> K&1;
}

2: turn(x,y)
根據(jù)偏移量 寫開關(guān)變換的狀態(tài)
3:時(shí)間復(fù)雜度

#include<bits/stdc++.h>
using namespace std;


string s[6],back_up[6];
const int N=6;  
//char s[N][N],back_up[N][N]; 
int n;                           
int dx[5]={0,-1,0,1,0};//枚舉五個(gè)方向 
int dy[5]={-1,0,1,0,0};

void turn(int x,int y)
{
    for(int i=0;i<5;i++){
        int xx=x+dx[i];
        int yy=y+dy[i];
        if(xx<0||xx>4||yy<0||yy>4)
            continue;
        if(back_up[xx][yy]=='0')
                back_up[xx][yy]='1';
        else back_up[xx][yy]='0'; 
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    cin>>n;
    while(n--){
        string s[5];
        for(int i=0;i<5;i++)
            cin>>s[i];
        int ans=10;
        for(int op=0;op<32;op++){ //注意這里枚舉的是操作數(shù)目 而不是第一行的狀態(tài) 
            //memcpy(back_up,s,sizeof(s));
            for(int i=0;i<5;i++)
                back_up[i]=s[i];  //用一個(gè)數(shù)組作為拷貝數(shù)組 用來改變?cè)瓟?shù)據(jù) 
            int step=0;           
            for(int i=0;i<5;i++){

            if(op>>i&1)
            {
                step++;
                turn(0,i);
            }
        }
            for(int i=0;i<4;i++){

                for(int j=0;j<5;j++){

                    if(back_up[i][j]=='0'){
                        step++;  //這一行的為0 下一行必須改變 
                        turn(i+1,j);
                    }

                }
            }
            bool dark=false;
            for(int i=0;i<5;i++){
                if(back_up[4][i]=='0')
                    {
                        dark=true;
                        break;
                }
            }
            if(!dark)
                ans=min(ans,step);
            for(int i=0;i<5;i++)
                back_up[i]=s[i];
        }
        if(ans>6)
            cout<<-1<<endl;
        else cout<<ans<<endl;

    }
    return 0;
} 

飛行員兄弟

  1. 每個(gè)開關(guān)只按一次
    2.順序是沒有關(guān)系的

解題思路
1、枚舉所有方案
2窑邦、按照方案對(duì)所有燈泡進(jìn)行操作
3擅威、判斷是否全亮->記錄方案

#include<bits/stdc++.h>
using namespace std;
const int N = 5;
char g[N][N], backup[N][N];
typedef pair<int, int> PII;
int get(int x, int y) {
    return x * 4 + y;  // 將 二維數(shù)組下標(biāo) 轉(zhuǎn)化為 0~15
}
void turn_one(int x, int y) {
    if (g[x][y] == '+')
        g[x][y] = '-';
    else g[x][y] = '+';
}
void turn_all(int x, int y) {
    for (int i = 0; i < 4; i++) {
        turn_one(x, i);// 行操作
        turn_one(i, y);//列操作
    }
    turn_one(x, y);// 由于x y 這個(gè)點(diǎn)操作了 兩次 所以再操作一次 將其恢復(fù)為操作一次
}
int main()
{
    for (int i = 0; i < 4; i++)
        cin >> g[i];
    vector<PII> res;  //記錄最終答案res
    for (int op = 0; op < (1 << 16); op++) {//枚舉每一種狀態(tài) 一共2^16種 1表示要按下這個(gè)開關(guān)一次
        vector<PII> temp;//每次枚舉新建一共存儲(chǔ)方案
        memcpy(backup, g, sizeof(g));//拷貝保留原數(shù)據(jù)


        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                if (op >> get(i, j) & 1) { //二維枚舉 每當(dāng)遇到一共1 就是一次操作
                    temp.push_back(make_pair(i, j));//沒操作一次 記錄再方案中
                    turn_all(i, j); //進(jìn)行操作
                }
            }
        }
        bool has_closed = false;  //判斷結(jié)果
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                if (g[i][j] == '+')
                    has_closed = true;
            }
        }
        if (has_closed == false) {

            //起初的res 是空的 所以要加入這個(gè)條件 讓此時(shí)的res被temp賦值
            //后面如果res的操作數(shù)是比temp多的 則更新 res
            if (res.empty() || res.size() > temp.size())
                res = temp;
        }
        memcpy(g, backup, sizeof(g));

    }
    cout << res.size() << endl;
    vector<PII>::iterator iter = res.begin(); //迭代器  遍歷輸出res
    for (iter; iter != res.end(); ++iter) 
        cout << iter->first + 1 << ' ' << iter->second + 1 << endl;


    return 0;
}

/*

翻硬幣

開始用了DFS 只能過一半
后來發(fā)現(xiàn)了規(guī)律,每個(gè)硬幣反兩次則無(wú)效
只需要從頭到尾枚舉一遍 不一樣就翻一次 就可
*/

#include<bits/stdc++.h>
using namespace std;
string s1, s2;
int len;
int ans = 0x3f3f3f3f;
void turn(int num)
{
    if (s1[num] == '*')
        s1[num] = 'o';
    else s1[num] = '*';
    if (s1[num + 1] == '*')
        s1[num + 1] = 'o';
    else s1[num + 1] = '*';
}
int main()
{
    cin >> s1 >> s2;
    len = s1.length();
    string backup = s1;
    int step = 0;
    for (int i = 0; i < len - 1; i++)
    {
        if (s1[i] != s2[i])
        {
            turn(i);
            step++;
        }
        if (s1 == s2)
            break;
    }
    cout << step << endl;
    system("PAUSE");
    return 0;
}

食物鏈

余1: 可以吃根節(jié)點(diǎn)
余2: 可以被根節(jié)點(diǎn)吃
余0:與根節(jié)點(diǎn)同類

余2吃余1 余0吃余2
所以%3余數(shù)相同的則是同一類 (關(guān)鍵)
帶權(quán)并查集精髓總結(jié):只要兩個(gè)元素在一個(gè)集合里面冈钦,通過它們與根節(jié)點(diǎn)的距離就能知道它們的相對(duì)關(guān)系郊丛。

#include<bits/stdc++.h>
using namespace std;
const int N = 5e4+10;
int n,k;
int p[N],d[N];
int find(int x) {
    if (p[x] != x) {
        int t = find(p[x]);
        d[x] += d[p[x]];
        p[x] = t;
    }
    return p[x];
}
int main()
{
    cin >> n >> k;
    int res = 0;
    for (int i = 1; i <= n; i++)
        p[i] = i;
    while (k--)
    {
        int t, x, y;
        cin >> t >> x >> y;
        if (x > n || y > n)res++; //如果有的點(diǎn)大于了n 則是假話
        else {
            int px = find(x), py = find(y);// px 是x的根節(jié)點(diǎn) y同理
            if (t == 1) {
                if (px == py && (d[x] - d[y]) % 3 != 0)//px==py 則x y 在同一個(gè)集合中
                     d[x]%3 應(yīng)該 和d[y]%3 相等 這樣x y 才是同類 -> (d[x]-d[y])%3==0
                    res++;
                else if (px != py) {
                    p[px] = py;   //如果不等則x y不在一個(gè)集合中
                    d[px] = d[y] - d[x];//d[x]+? %3 =d[y]%3 -> ?(d[px])=d[y]-d[x] 要使x y是同類則滿足這個(gè)方程
                }//可以畫圖
            }
            else {
                if (px == py && (d[x] - d[y] - 1) % 3)res++;//如果px=py 則x y在同一集合 x吃y 則d[x]%3 -d[y]%3==1 ->(d[x]-d[y]-1)%3=0;
                else if (px != py) {
                    p[px] = py; //如果不在同一集合 則把x并入y集合中 (d[px]+? )%3=d[y]%3+1;
                    d[px] = d[y] + 1 - d[x];
                }
            }
        }

    }
    cout << res << endl;
    system("PAUSE");
    return 0;
}

堆: 手寫堆

一.堆操作

  1. 插入一個(gè)數(shù)
    2.求這個(gè)集合當(dāng)中的最小值
    3.刪除最小值
    上面三個(gè)是STL 中可以實(shí)現(xiàn)
  2. 刪除任意一個(gè)元素
    5.修改任意一個(gè)元素
    后兩個(gè)操作只有手寫堆才可以實(shí)現(xiàn)
    二.堆描述
    堆是完全二叉樹 除了最后一層節(jié)點(diǎn)之外 上面所有節(jié)點(diǎn)都是滿的
    最后一個(gè)節(jié)點(diǎn)從左到右排布

三.堆的構(gòu)造:
堆的一個(gè)點(diǎn)小于等于左右兒子(左右兒子大小不一定)
根節(jié)點(diǎn)為堆的最小值
注意下標(biāo)從1開始

四.堆存儲(chǔ)
使用一個(gè)一維數(shù)組存儲(chǔ)
x的做兒子: 2x
x的右兒子:2x+1;

五.核心操作
down

up

heap 堆 size 堆的大小 
1. 插入操作 
heap[++size]=x; up(size);
2.求當(dāng)前堆的最小值 
heap[1]  ,堆頂一定是最小的 
3. 刪除最小值 
用整個(gè)堆的最后一個(gè)元素覆蓋掉堆頂元素 size-- 再down
(因?yàn)橐痪S數(shù)組刪除頭非常困難,但是刪除尾部很容易) 
heap[1]=heap[size];size--; dowx(1);
4. 刪除任意一個(gè)元素
heap[k]=heap[size];size--;down(k);up(k); //只會(huì)執(zhí)行down up 其中一個(gè) 
5.修改任意一個(gè)元素
heap[k]=x;down(k);up(k); 


void down(int u){
   int t=u;
   如果有左兒子 且 左兒子小于頂點(diǎn) 則把左兒子變成頂點(diǎn) 
   if(u*2<=size&&h[u*2]<h[t])t=u*2;
   如果有右兒子 且 右兒子小于頂點(diǎn) 則把右兒子變成頂點(diǎn) 
   if(u*2+1<=size&&h[u*2+1]<h[t])t=u*2+1;
   如果 u改變了 交換當(dāng)前最小值和u的值 便且再down一下t 
   if(u!=t){
       swap(h[u],h[t]);
       down(t);
   }
   
} 

void up(int u)
{
   //u/2 同時(shí)包括了左二字和有兒子 2/2=1 3/2=1; 
   while(u/2&&h[u/2]>h[u]){
       swap(h[u/2],h[u]);// 如果大了就交換 
       u/=2; //每次u往上走 
   }
}

例1 堆排序

ph[]從下標(biāo)到堆里 hp[]從堆到下標(biāo)里 
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int h[N],size1;
int n,m;
void down(int u)
{
    int t=u;
    if(u*2<=size1&&h[u*2]<h[t])t=u*2;
    if(u*2+1<=size1&&h[u*2+1]<h[t])t=u*2+1;
    if(u!=t){
        swap(h[u],h[t]);
        down(t);
    } 
    
    
}

int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    cin>>h[i];
    size1=n;
    for(int i=n/2;i;i--)
        down(i);
    while(m--)
    {
        cout<<h[1]<<" ";
        h[1]=h[size1];
        size1--;
        down(1);
    }
    return 0;
} 

例2: 模擬堆

#include<bits/stdc++.h>
using namespace std;con1
const int N=100010;
int h[N],ph[N],hp[N],cnt;
void heap_swap(int a,int b)
{
    swap(ph[hp[a]],ph[hp[b]]);
    swap(hp[a],hp[b]);
    swap(h[a],h[b]);
}
void down(int u){
    
    int t=u;
    if(u*2<=cnt&&h[u*2]<h[t])t=u*2;
    if(u*2+1<=cnt&&h[u*2+1]<h[t])t=u*2+1;
    if(u!=t){
        heap_swap(u,t);
        down(t);
    }
}
void up(int u)
{
    while(u/2&&h[u]<h[u/2]){
        heap_swap(u,u/2);
        u/=2;
    }
    
}
int main()
{
    int n,m=0;//m當(dāng)前第幾個(gè)插入的數(shù) 
    cin>>n;
    while(n--){
        string op
        int k,x;
        cin>>op;
        int k,x;
        if(op=="I"){
            cin>>x;
            cnt++;
            m++;
            ph[m]=cnt;
            hp[cnt]=m;
            h[cnt]=x;
            up(cnt);
        }
        else if(op=="PM")cout<<h[1]<<endl; 
        else if(op=="DM"){
        heap_swap(1,cnt);
        cnt--;
        down(1);
         
    }
    else if(op=="D")
    {
        cin>>k;
        k=ph[k];
        heap_swap(k,cnt);
        cnt--;
        up(k);
        down(k);
    }else {
        cin>>k>>x;
        k=ph[k];
        h[k]=x;
        up(k);
        down(k);
    }
    
    return 0;
} 

哈希表 (散列表)

一、 存儲(chǔ)結(jié)構(gòu)
1.開放尋址法
2.拉鏈法

二瞧筛、 字符串哈希方式

大范圍的集合 映射到 從0~N

x mod 1e5 模要取一個(gè)質(zhì)數(shù) (沖突的概率欣魇臁)
可能會(huì)產(chǎn)生沖突

1.拉鏈法:開一個(gè)一維數(shù)組 存儲(chǔ)哈希的值

添加 找h(x) 插入到鏈上
查找 找h(x) 遍歷鏈看有沒有

  1. 開放尋址法
    1.添加 先找到h[k] 如果h[k] 有人 則往后找 直到?jīng)]有人

2.查找 第k個(gè)坑位開始 從前往后找
如果當(dāng)前位置有人,是x 則找到
如果當(dāng)前位置有人较幌,不是x 則繼續(xù)找下一個(gè)
如果當(dāng)前位置沒人揍瑟,則x不存在

hash表的刪除一般不會(huì)直接刪除 一般都是 把刪除的元素做特殊的標(biāo)記
而且一般不會(huì)用到刪除操作

開放尋址法的核心操作
find函數(shù):如果x在hash表中存在,則返回x的位置
如果x不存在則返回x應(yīng)該存儲(chǔ)的位置

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int mod=2e5+3;
int null =0x3f3f3f3f;
int h[N];
int find(int x)
{
    int t=(x%mod+mod)%mod;//為了防止余數(shù)是負(fù)數(shù)
    while(h[t]!=null&&h[t]!=x){//如果這個(gè)坑是空了 就不存在x了
                                //如果這個(gè)坑==x了 那就找到了
        
        t++; //繼續(xù)找下一個(gè)坑
        if(t==N) //如果到末尾了 那就從頭開始找
            t=0;
    }
    return t; //返回值是 x應(yīng)該放的位置 或者是找到x的位置
}
int main()
{
    int n;
    cin>>n;
    memset(h,0x3f,sizeof(h));//首先將數(shù)組置為null 作為判斷是否為空的標(biāo)志
    while(n--){
        string op;
        int x;
        cin>>op>>x;
        if(op=="I"){
         //  h[x]=find(x); 
           h[find(x)]=x; // 返回的是x應(yīng)該放的位置  讓哈希表中的這個(gè)位置=x
        }
        else {
            if(h[find(x)]==null)//返回的值如果是空 則沒有找到
                cout<<"No"<<endl;
            else cout<<"Yes"<<endl;//否則就是找到了 不存在別的情況
            
        }
        
    }
    
    return 0;
}

二. 字符串哈希

字符串前綴哈希法

核心 在p 進(jìn)制的角度將一個(gè)字符串看成是一個(gè)數(shù)字

如何來定義每一個(gè)前綴的哈希
將字符串看成是P進(jìn)制數(shù)
不要某一個(gè)字符映射成0 因?yàn)?的p進(jìn)制還是0
假定不存在沖突 不考慮沖突

經(jīng)驗(yàn)值 p進(jìn)制為 p=131, mod 為2^64;
這里用usigned long long 來實(shí)現(xiàn) 溢出就是對(duì)mod取模

核心操作:
1.hash的處理
h[i]=h[i-1]p+str[i];
2.求 l 到 r 之間的字符串
h[R]-h[L]
p^(R-L+1)
R-L+1=R-1-(L-2);
h[L-1] 從 p^0 ~ p^(L-2)
h[R] 從 p^0 ~p^(R-1)

預(yù)處理一個(gè)數(shù)的n次方

int p[N];
p[0]=1; //p的0次方為1;
for(int i=1;i<=n;i++)
p[i]=p[i-1]*p;

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
int p=131;// 131 是 經(jīng)驗(yàn)值
const int N=1e5+10;
ull pow1[N],h[N];// unsigned long long 溢出 就是mod 2^64  
char str[N];
ull hash1(int l,int r)
{
    return h[r]-h[l-1]*pow1[r-l+1]; //核心操作
    //求出l~r 區(qū)間內(nèi)的hash值
}
int main()
{
    int n,m;
    cin>>n>>m>>str+1;//注意從下標(biāo)1開始存儲(chǔ)字符
    pow1[0]=1; // p的零次冪等于1
    for(int i=1;i<=n;i++)
    {
        pow1[i]=pow1[i-1]*p;//對(duì)p的次方 進(jìn)行預(yù)處理操作
    }

    for(int i=1;i<=n;i++)
    {
        h[i]=h[i-1]*p+str[i];//核心操作 得到每個(gè)字符的hash值
    }
    while(m--){
        int l1,r1,l2,r2;
        cin>>l1>>r1>>l2>>r2;
        if(hash1(l1,r1)==hash1(l2,r2))//如果兩個(gè)區(qū)間的hash值相等 則兩個(gè)字符串相同
        cout<<"Yes"<<endl;
        else cout<<"No"<<endl; 
    }
    
    return 0;
}

重點(diǎn) C++的STL

vector
變長(zhǎng)數(shù)組
倍增的思想
系統(tǒng)為某一程序分配空間時(shí),所需時(shí)間與空間大小無(wú)關(guān)
與申請(qǐng)次數(shù)有關(guān)

#include<vector>

int main()
{
   vector<int> a;// 定義一個(gè)vector
   vector<int> a(10);//定義一個(gè)長(zhǎng)度為10的vector
   vector<int> a(10,3);//長(zhǎng)度為10 每個(gè)元素為3

   vector<int> a[10];//定義一個(gè)vector數(shù)組
   
   
    注意 size();和empty();是所有容器都有的函數(shù)
     時(shí)間復(fù)雜度為O(1); 
    a.size();//vector中元素的個(gè)數(shù)
    a.empty();//vector是否為空 
    
    并不是所有容器都有clear();比如隊(duì)列就沒有 
    a.clear(); //清空vector 
    
    a.front(); //返回vector的第一個(gè)數(shù) .
    a.back();//返回vector的第二個(gè)數(shù)
    
    a.push_back();//向vctor的最后插入一個(gè)樹 
    a.pop_back();//刪除vector的最后一個(gè)樹
    
    //迭代器
     a.begin();// vector的第0個(gè)數(shù) a[0]
     a.end();//vector的最后一個(gè)數(shù)的后面一個(gè)數(shù)a[a.size()];
     
    遍歷方式
    for(int i=0;i<10;i++)a.push_back(i);
    
    1.for(int i=0;i<a.size();i++)cout<<a[i]<<" ";
    cout<<endl;
    
    2.for(vector<int>::iterator iter=a.begin();i!=a.end();i++)
    cout<<*i<<" ";
    cout<<endl;       
    3.范圍遍歷(C++11)
      for(auto x:a)cout<<x<<' ';  
      cout<<endl;
      
    vector 同樣支持比較 大于小于等于  按照字典序 
   vector<int> a(10,3);//長(zhǎng)度為10 每個(gè)元素為3
   vector<int> b(10,3);
   if(a==b)
       cout<<"a=b"<<endl;
    for(int i=0;i<10;i++)
       cout<<a[i]<<endl;
       return 0;  

} 
pair
pair<int,string> p;
p.first;//取得第一個(gè)元素 
p.second;//取得第二個(gè)元素 
支持比較運(yùn)算 按照字典序 以 first 為第一關(guān)鍵字,以second為第二關(guān)鍵字

p=make_pair(10,"yxc");
C++11 :p={20,"abc"};

pair<int,pair<int,int> > //有三個(gè)屬性時(shí)
與結(jié)構(gòu)體相比 可以自帶比較函數(shù)
 

 
string
字符串
size();
empty(); 
clear();
length();// 作用和size一模一樣 
支持加減
string a="WD";
a+="def";//加上一段字符串 
a+="b";   //加上一個(gè)字符
 
cout<<a.substr(1,3)<<endl;
 cout<<substr(1,3)<<endl;
substr();  功能返回字符串的一個(gè)字串
 第一個(gè)參數(shù): 字符串中的一個(gè)下標(biāo)
 第二個(gè)參數(shù):從這個(gè)下標(biāo)開始多少長(zhǎng)度 
 第二個(gè)參數(shù)可以省略,返回到末尾的字串
使用printf輸出
printf("%s\n",a.c_str());
c_str();返回字符串存儲(chǔ)的第一個(gè)地址
 
 
queue
隊(duì)列
push();//向隊(duì)尾插入元素 
front();//返回隊(duì)頭元素 
back();//返回隊(duì)尾元素 
pop(); //彈出隊(duì)頭元素
size();
empty(); 
 無(wú)clear();函數(shù)
  如果想1清空一個(gè)q 直接重新構(gòu)造一個(gè) q=queue<int>(); 

priority_queue
優(yōu)先隊(duì)列(堆)
 push();//向堆中插入一個(gè)元素 
 top();//返回堆頂元素 
 pop();//彈出堆頂元素
 
  一般默認(rèn)大根堆 

也無(wú)clear函數(shù) 
priority_queue<int> pq;
 

如果小變成小根堆
1. 插入的時(shí)候直接插入-x
2. 定義直接定義成小根堆
 priority_queue<int,vector<int>,greater<int> >pq;
  




stack
棧 
也無(wú)clear 函數(shù) 
push();//向棧頂插入一個(gè)元素 
top();//返回棧頂元素 
pop(); //彈出棧頂元素 


deque 加強(qiáng)版的vector 
雙端隊(duì)列 
隊(duì)頭隊(duì)尾都可以插入刪除
(加強(qiáng)版的vector) 
size();
empty();
clear();
 front();//查詢第一個(gè)元素 
 back();//查詢最后一個(gè)元素 
 push_back();//向隊(duì)尾插入一個(gè)元素 
 pop_back();//彈出隊(duì)尾元素 
 push_front();//向隊(duì)首插入一個(gè)元素 
 pop_front();//彈出隊(duì)首元素 
 支持隨機(jī)尋址
 begin();
 end(); 
 deque功能十分強(qiáng)大 但是速度比較慢
  



set,map,multiset,multimap
基于紅黑樹實(shí)現(xiàn)的,動(dòng)態(tài)的維護(hù)一個(gè)有序的序列 
紅黑樹是平衡二叉樹的一種 
set<int> s;//不可語(yǔ)有重復(fù)元素 
multiset<int> ms; 可以有重復(fù)元素 
size();
empty();
clear();
insert();//插入一個(gè)數(shù)
find();//查找一個(gè)數(shù)
 count();//返回一個(gè)數(shù)的個(gè)數(shù) set為1
 erase():
  (1)如果輸入一個(gè)數(shù)x 則刪除所有x
  (2)如果輸入一個(gè)迭代器,則刪除這個(gè)迭代器
set的核心操作: 
lower_bound();返回大于等于x的最小的迭代器 
upper_bound(); 返回大于x的最小的迭代器 
如果沒有則返回end();
 
map  
multimap

insert();//輸入的參數(shù)是一個(gè)pair 
erase();//輸入的參數(shù)是pair或者迭代器

 
 可像數(shù)組一樣使用map
 map<string,int> m;
 a["WD"]=1;
 cout<<a["WD"]<<endl; 
 支持lower_bound();upper_bound();
  


unordered_set  
unordered_map
unordered_multiset
unordered_multimap
哈希表實(shí)現(xiàn) 
內(nèi)部無(wú)序 
和上面操作一樣 時(shí)間復(fù)雜度是o(1),比上面速度快
不支持 lower_bound();upper_bound(); 
不支持 迭代器的++ --;
 

bitset 位存儲(chǔ)
壓位 

bitset<10000> s;
~,&^|;
>>,<<
==,!=;
[];
count();返回有多少個(gè)1 
set(); 把所有為置為1
set(k,v); 把第k位變成v
reset();把所有位置0
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末乍炉,一起剝皮案震驚了整個(gè)濱河市绢片,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌岛琼,老刑警劉巖底循,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異槐瑞,居然都是意外死亡此叠,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門随珠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人猬错,你說我怎么就攤上這事窗看。” “怎么了倦炒?”我有些...
    開封第一講書人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵显沈,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我逢唤,道長(zhǎng)拉讯,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任鳖藕,我火速辦了婚禮魔慷,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘著恩。我一直安慰自己院尔,他們只是感情好蜻展,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著邀摆,像睡著了一般纵顾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上栋盹,一...
    開封第一講書人閱讀 52,475評(píng)論 1 312
  • 那天施逾,我揣著相機(jī)與錄音,去河邊找鬼例获。 笑死汉额,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的躏敢。 我是一名探鬼主播闷愤,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼件余!你這毒婦竟也來了讥脐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤啼器,失蹤者是張志新(化名)和其女友劉穎旬渠,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體端壳,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡告丢,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了损谦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片岖免。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖照捡,靈堂內(nèi)的尸體忽然破棺而出颅湘,到底是詐尸還是另有隱情,我是刑警寧澤栗精,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布闯参,位于F島的核電站,受9級(jí)特大地震影響悲立,放射性物質(zhì)發(fā)生泄漏鹿寨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一薪夕、第九天 我趴在偏房一處隱蔽的房頂上張望脚草。 院中可真熱鬧,春花似錦原献、人聲如沸玩讳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)熏纯。三九已至同诫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間樟澜,已是汗流浹背误窖。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留秩贰,地道東北人霹俺。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像毒费,于是被迫代替她去往敵國(guó)和親丙唧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361