原題連結:https://www.luogu.com.cn/problem/P5522
題意解讀:有若干0/1/?組成的字串,支援兩種操作:1.將制定位置字串修改成新字串;2.查詢區間內字串能否統一成一個字串,求有多少種可能;將2的所有結果異或起來,再和0異或,輸出最終答案。注意:?表示可以用0或1取代。
解題思路:單點修改,區間查詢,可以用線段樹解決。
關鍵看線段樹節點維護什麼資訊?
字串要能統一成一個,對應位置字元必須符合:1.都是'1' 2.都是'0' 3.有一個是'?'
對於1/2是固定的,對於3,如果一個是'?'一個是非'?'那麼情況也是唯一的,如果兩個都是'?'那麼既可以都是'1'又可以都是'0',這樣就會有2中可能
因此對於兩個字串能統一成多少種相同的串,就是要找到有多少個位置都是'?',設有x個,則一共有2^x中可能。
如果直接維護字串,在進行pushup等操作的時候每次常數都比較高,需要一種更最佳化的方式,如果將對應位置的值對映到二進位制就好處理了。
方式如下:
設v1是一個整數,其二進位制表示一個01?字串中確定是'1'的位置為1,如0?1,第2個位置是'1',那麼v1二進位制為100,v1=4
設v2是一個整數,其二進位制表示一個01?字串中確定是'0'的位置為1,如0?1,第0個位置是'0',那麼v2二進位制為001,v2=1
有了以上定義,
1、在進行節點合併時,root.v1 = left.v1 | right.v1,root.v2 = left.v2 | right.v2
2、在進行區間查詢時,直接返回合併後的結果即可,再進一步進行判斷和統計?的個數,
- 判斷邏輯:如果v1 & v2不為0,說明多個字串相同位置既有'1'又有'0',不可能統一成一種,結果是0
- 統計方式:如果v1 & v2為0,將v1 | v2,統計其中字串長度範圍內0的個數x,即表示'?'的個數,再取2^x,即表示可以統一成多少種字串
3、單點修改操作就是常規的修改,注意將字串轉化成v1、v2
具體細節參考程式碼。
100分程式碼:
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
struct Node
{
int l, r;
int v1; //v1的二進位制表示中,[l,r]區間內所有字串位置i為'1'才是1
int v2; //v1的二進位制表示中,[l,r]區間內所有字串位置i為'0'才是1
} tr[N * 4];
string a[N];
int n, m, q, ans;
void pushup(Node &root, Node &left, Node &right)
{
root.v1 = left.v1 | right.v1;
root.v2 = left.v2 | right.v2;
}
void pushup(int u)
{
pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
}
void build(int u, int l, int r)
{
tr[u] = {l, r};
if(l == r)
{
for(int i = 0; i < a[l].length(); i++)
{
if(a[l][i] == '1') tr[u].v1 |= (1 << i);
else if(a[l][i] == '0') tr[u].v2 |= (1 << i);
}
}
else
{
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
}
Node query(int u, int l, int r)
{
if(tr[u].l >= l && tr[u].r <= r) return tr[u];
else if(tr[u].l > r || tr[u].r < l) return Node{};
else
{
Node res = {};
Node left = query(u << 1, l, r);
Node right = query(u << 1 | 1, l, r);
pushup(res, left, right);
return res;
}
}
void update(int u, int pos, string t)
{
if(tr[u].l == tr[u].r)
{
tr[u].v1 = tr[u].v2 = 0; //先清空
for(int i = 0; i < t.length(); i++)
{
if(t[i] == '1') tr[u].v1 |= (1 << i);
else if(t[i] == '0') tr[u].v2 |= (1 << i);
}
}
else
{
int mid = tr[u].l + tr[u].r >> 1;
if(pos <= mid) update(u << 1, pos, t);
else update(u << 1 | 1, pos, t);
pushup(u);
}
}
int main()
{
cin >> n >> m >> q;
for(int i = 1; i <= m; i++) cin >> a[i];
build(1, 1, m);
int op, l, r, pos;
string t;
while(q--)
{
cin >> op;
if(op == 0)
{
cin >> l >> r;
Node res = query(1, l, r);
if(res.v1 & res.v2) //說明有'1'的位置也有'0',不能統一成一個字串
{
ans ^= 0; //結果是0
}
else
{
int x = res.v1 | res.v2;
int cnt = 0;
for(int i = 0; i < n; i++)
{
if(x >> i & 1) cnt++; //統計確定是'1'或'0'的位置數量
}
int ques = n - cnt; //'?'的個數
ans ^= (1 << ques); // 2^ques即有多少種字串組合
}
}
else
{
cin >> pos >> t;
update(1, pos, t);
}
}
cout << ans;
return 0;
}