【6.24校內test】T3 棠梨煎雪

【題目背景】c++

歲歲花藻檐下共將棠梨煎雪。ide

自總角至你我某日展轉天邊。ui

天淡天青,宿雨沾襟。this

一年一會信箋卻只見寥寥數言。spa

——銀臨《棠梨煎雪》指針

【問題描述】code

扶蘇正在聽《棠梨煎雪》的時候,山東省實驗中學的 zzh 發來一道IOI2018集訓隊 互測題:我切了這集訓隊題,辣雞扶蘇快過來作題。扶蘇定睛一看,這不 s* 題嘛,寫 了一發交上去才發現本身看錯題目了。可是寫完的代碼不能浪費,因而就有了這道題。blog

歌詞中的主人公與她的朋友一年會有一次互相寫信給對方,一共通訊了 m 年。爲了 簡化問題,咱們認爲他們每封信的內容都是一條二進制碼,而且全部二進制碼的長度都 是 n。即每封信的內容都是一個長度爲 n 的字符串,這個字符串只含字符 0 或 1。遞歸

這天她拿出了朋友寫給她的全部信件,其中第 i 年的寫的信件編號爲 i。因爲信件 保存時間太久,上面有一些字符已經模糊不清,咱們將這樣的位置記爲 ?,? 字符能夠 被解釋爲 0 或 1。因爲她的朋友也是人,符合人類的本質,因此朋友在一段連續的時間 中書寫的內容多是相同的。如今她想問問你,對於一段連續的年份區間 [l,r] 中的所 有信件,假如朋友在這段時間展現了人類的本質,所寫的是同一句話,那麼這一句話一 共有多少種可能的組成。也即一共有多少字符串 S,知足在這個區間內的全部信件的內 容均可能是 S。字符串

一個長度爲 n 的只含 0,1,? 的字符串 A 多是一個字符串 B 當且僅當 B 知足如 下條件:

  1. B 的長度也是 n
  2. B 中只含字符 0,1
  3. A 中全部爲 0 的位置在 B 中也是 0
  4. A 中全部爲 1 的位置在 B 中也是 1
  5. A 中爲 ? 的位置在 B 中能夠爲 0 也能夠是 1

同時她可能會忽然發現看錯了某年的信的內容,因而他可能會把某一年的信的內容 修改成一個別的只含 0,1,? 的長度爲 n 的字符串。

【輸入格式】

輸入文件名爲 lin.in。

輸入文件中有且僅有一組數據,第一行爲三個正整數 n,m,q,表明字符串長度,字 符串個數和操做次數。

下面 m 行,每行是一個長度爲 n 的字符串,第 i 個字符串表明第 i 年信的內 容。

下面 q 行,每行的第一個數字是操做編號 opt。

若是 opt=0,那麼後面接兩個整數 [l,r],表明一次查詢操做

若是 opt=1,那麼後面接一個整數 pos,在一個空格後會有一個長度爲 n 的字符 串,表明將第 pos 個字符串修改成新的字符串。

【輸出格式】

輸出文件名爲 lin.out。

對於每一個查詢操做,輸出一行一個整數表明答案。

 


SOLUTION:

子任務 1: 0 次詢問,直接 freopen 便可得分,指望得分 5 分。

子任務 2: 考慮在詢問的時候枚舉全部可能出現的串,而後遍歷區間全部的字符串看看是否 合法。因爲字符串長度爲 n,且每一個位置只多是 0/1,因此一共有 2 n 中狀況。時間 複雜度 O(2 nmq),指望得分 10 分。

子任務 3: 考慮對於一段區間的全部字符串的第 x 個字符,一共有四種可能,肯定爲 0,確 定爲 1,均可以,都不能夠。若是都不能夠則輸出0,若是肯定爲 0 或 1,則這一位 只有一種可能,不然有兩種可能,根據乘法原理,若是有 a 個位置均可以,則會有 2 a 種可能。輸出答案便可。時間複雜地 O(nmq),指望得分 15 分。

(感受個人代碼就是介個思路呀,可是我腫麼沒有分呢??又玄學啦(改了幾行吧,就對了可海星))

(啊通過一夜不懈努力,我終於知道我錯哪了:1.cnt1沒有memset,而後l==r的特判也不到家(因此emm對本身無語惹))

45pts(數據水的說qwq)

#include<bits/stdc++.h>

using namespace std;

inline int read(){
    int ans=0;
    char last=' ',ch=getchar();
    while(ch<'0'||ch>'9') last=ch,ch=getchar();
    while(ch>='0'&&ch<='9') ans=ans*10+ch-'0',ch=getchar();
    if(last=='-') ans=-ans;
    return ans;
}

int n,m,q,opt,l,r;
char s[100008][31];

int cx(int l,int r){
    long long cnt1;
    int ans=1;
    int qj=r-l+1;
    for(int i=1;i<=n;i++){
        char d=' ';
        cnt1=0;
        for(int j=l;j<=r;j++){
            if(s[j][i]=='?'){
                cnt1++;
                continue;
            }
            if(d==' ') d=s[j][i];
            if(d==s[j][i]) continue;
            else return 0;
        }
        if(cnt1==qj) ans*=2;
    }
    return ans;
}

int main(){
    n=read();m=read();q=read();
    if(m==1)
        return 0;
    for(int i=1;i<=m;i++)
      scanf("%s",s[i]+1);
    for(int i=1;i<=q;i++){
        opt=read();
        if(opt==0){
            l=read();r=read();
            cout<<cx(l,r)<<"\n";
        }
        else {
            char ss[31];
            int f=read();
            scanf("%s",ss+1);
            for(int i=1;i<=n;i++){
                s[f][i]=ss[i];
            }
        }
    }
    
    return 0;
} 
45pts Code
#include<bits/stdc++.h>

using namespace std;

inline int read(){
    int ans=0;
    char last=' ',ch=getchar();
    while(ch<'0'||ch>'9') last=ch,ch=getchar();
    while(ch>='0'&&ch<='9') ans=ans*10+ch-'0',ch=getchar();
    if(last=='-') ans=-ans;
    return ans;
}

int n,m,q,opt,l,r;
char s[100008][31];

int cx(int l,int r){
    int cnt1[31];
    memset(cnt1,0,sizeof(cnt1));
    if(l==r) {
        int cnt=1;
        for(int i=1;i<=n;i++)
            if(s[l][i]=='?') cnt*=2;
        return cnt;
    }
    int k=l;
    for(int i=1;i<=n;i++){
        char d='x';
        for(int j=l;j<=r;j++){
            if(s[j][i]=='?'){
                cnt1[i]++;
                continue;
            }
            if(d=='x') d=s[j][i];
            if(d==s[j][i]) continue;
            else return 0;
        }
    }
    int ans=1;
    int qj=r-l+1;
    for(int i=1;i<=n;i++){
        if(cnt1[i]==qj) ans*=2;
        else ans*=1;
    }
    return ans;
}

int main(){
    freopen("lin.in","r",stdin);
    freopen("lin.out","w",stdout);
    n=read();m=read();q=read();
    if(m==1)
        return 0;
    for(int i=1;i<=m;i++)
      scanf("%s",s[i]+1);
    for(int i=1;i<=q;i++){
        opt=read();
        if(opt==0){
            l=read();r=read();
            cout<<cx(l,r)<<"\n";
        }
        else {
            char ss[31];
            int f=read();
            scanf("%s",ss+1);
            for(int i=1;i<=n;i++){
                s[f][i]=ss[i];
            }
        }
    }
    
    return 0;
} 
最少改動的45pts Code

好了接下來是正解作法了(我不保證我能看懂由於zay太愛用指針啦

的一部分(忽然發現還有好多子任務的說

子任務 4:

    考慮 n 只有 30,能夠狀壓到 int 中,具體的,維護兩個 int,第一個 int 維 護對應位是否肯定爲 0 或 1,第二個 int 維護若是肯定爲 0 或 1 了則具體是 0 還 是 1。經過位運算操做一下能夠快速的該區間內的信息。

    具體的,考慮 a1,a2 是從 L 到某個位置左側的區間信息,b1,b2 是該位置的區 間信息,若是該詢問沒有能匹配的字符串,則 (a1|b1)&(a2^b2) 應該不爲 0,輸出 0 便可。不然 a1=a1|b1;a2=(a1|b1)&(a2&b2)。

    驗題人的神仙作法是維護一個 longlong,兩個二進制位一組,01 表明肯定爲 0,10表明肯定爲 1,11 表明均可以,00 表明不合法,維護的時候直接按位與便可。 時間複雜度 O(mq),指望得分 15 分。

子任務 5:

考慮 n 只有 1,能夠開一個線段樹維護每一個位置的字符是什麼,pushup的時候操 做一下便可。時間複雜度 O(q logm),指望得分 15 分。

子任務 6:

世界上沒有什麼事情是開一棵線段樹不能解決的。若是有,那就開 30 棵。時間 複雜度 O(nq logm),指望得分 10 分。

子任務 7:

考慮用一棵線段樹維護子任務 4 中的狀壓信息,經過位運算能夠把 n 省掉,於 是總複雜度 O(nq + qlogm),指望得分 30 分。

#include <cstdio>

template <typename T>
inline void qr(T &x) {
  char ch = getchar(), lst = ' ';
  while ((ch > '9') || (ch < '0')) lst = ch, ch=getchar();
  while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
  if (lst == '-') x = -x;
}

const int maxn = 100010;

int n, m, q;
char s[maxn][35];

#ifdef ONLINE_JUDGE
int Ans;
#endif

struct Tree {
  Tree *ls, *rs;
  int l, r, x/*int1*/, y/*int2*/;
  bool leg;//判斷 

  Tree() {
    ls = rs = NULL;
    l = r = x = y = 0;
    leg = true;
  }

  void pushup() {
    if (!(this->ls->leg && this->rs->leg)) {//左右子樹都不合法 
      this->leg = false;
    } else {
      if ((this->ls->x & this->rs->x) & (this->ls->y ^ this->rs->y)) {
          //只有當左右兒子對應位置是否全爲1或0都相同而且左右兒子對應的y不相同時,顯然不合法 
        this->leg = false;
      } else {
        this->leg = true;
        this->x = this->ls->x | this->rs->x;
        this->y = this->ls->y | this->rs->y;
      }
    }
  }
};
Tree *rot;

void ReadStr(char *p);
void Update(const int x);
void Query(const int l, const int r);
void update(Tree *const u, const int p);
Tree query(Tree *u, const int l, const int r);
void build(Tree *const u, const int l, const int r);

int main() {
  freopen("lin.in", "r", stdin);
  freopen("lin.out", "w", stdout);
  qr(n); qr(m); qr(q);
  for (int i = 1; i <= m; ++i) {
    ReadStr(s[i] + 1);//讀入一串字符串的神仙操做 
  }
  build(rot = new Tree, 1, m);
  int opt, l, r;
  while (q--) {
    opt = 0; qr(opt);
    if (opt == 0) {
      l = r = 0; qr(l); qr(r);
      Query(l, r);
    } else {
      l = 0; qr(l);
      ReadStr(s[0] + 1);
      Update(l);
    }
  }
#ifdef ONLINE_JUDGE
  printf("%d\n", Ans);
#endif
  return 0;
}

void ReadStr(char *p) {
  do *p = getchar(); while ((*p != '0') && (*p != '1') && (*p != '?'));
  do *(++p) = getchar(); while ((*p == '0') || (*p == '1') || (*p == '?'));
  *p = 0;
}

void build(Tree *const u, const int l, const int r) {
  if ((u->l = l) == (u->r = r)) {//葉節點 
    for (int i = 1; i <= n; ++i) {//直接掃描記錄便可 
      if (s[l][i] != '?') {
        u->x |= 1 << i;
        if (s[l][i] == '1') {
          u->y |= 1 << i;
        }
      }
    }
  } else {//不是葉節點,遞歸建樹; 
    int mid = (l + r) >> 1;
    build(u->ls = new Tree, l, mid);
    build(u->rs = new Tree, mid + 1, r);
    u->pushup();
  }
}

Tree query(Tree *u, const int l, const int r) {
  if ((u->l > r) || (u->r < l)) return Tree();
  if ((u->l >= l) && (u->r <= r)) return *u;
  Tree _ret;
  auto ll = query(u->ls, l, r), rr = query(u->rs, l, r);
  _ret.ls = &ll; _ret.rs = &rr;
  _ret.pushup();
  return _ret;
}

void Query(const int l, const int r) {
  auto _ret = query(rot, l, r);
  if (!_ret.leg) {
#ifndef ONLINE_JUDGE
    puts("0");
#endif
  } else {
    int ans = 1;
    for (int i = 1; i <= n; ++i) if (!(_ret.x & (1 << i))) {
      ans <<= 1;
    }
#ifdef ONLINE_JUDGE
    Ans ^= ans;
#else
    printf("%d\n", ans);
#endif
  }
}

void update(Tree *u, const int p) {
  if (u->ls) {
    if (u->ls->r >= p) {
      update(u->ls, p);
    } else {
      update(u->rs, p);
    }
    u->pushup();
  } else {
    *u = Tree();
    u->l = u->r = p;
    for (int i = 1; i <= n; ++i) {
      if (s[0][i] != '?') {
        u->x |= 1 << i;
        if (s[0][i] == '1') {
          u->y |= 1 << i;
        }
      }
    }
  }
}

void Update(const int x) {
  update(rot, x);
}
我盡力了,但我真的沒有全看懂

end-

相關文章
相關標籤/搜索