出題人:辰星凌
驗題人:無
題目傳送門:薇爾莉特
給出乙個 \(n\) 行 \(m\) 列的矩陣(最初全為 \(0\)),有 \(t\) 個操作,每次操作選出乙個子矩陣,將其中的所有元素都對 \(x\) 進行一次 \(or\),求最後的矩陣。
純暴力,沒有坑點,按照題意模擬一下就可以了。
時間複雜度:\(o(tn^2)\) 。
分數:\(20pt\) 。
【code #1】
#include#include#include#define re register int
#define ll long long
using namespace std;
const int n=503,p=1e9+7;
int n,m,a,b,c,d,x,t,hyj,ans1,ans2,a[n][n];
inline void in(re &x)
int main()
for(re i=1;i<=n;++i)
for(re j=1;j<=m;++j)
(ans1+=a[i][j])%=p,ans2^=a[i][j];
printf("%d %d %d\n",ans1,ans2,(ll)ans1*ans2%p);
}
如果把取「或」換成「異或」就是乙個區修單查的二維樹狀陣列板子,可惜 \(or\) 不滿足可減性。
那如果如果它分為 \(log\) 個二進位制位呢?
對於任意乙個二進位制位,都是滿足可減性的,這下子可以放心地上 \(\text\) 了。
直接開 \(log\) 棵二維 \(\text\),分別單獨處理所有的二進位制位,每次操作暴力拆分 \(x\) 進行 \(log\) 次區間修改,最後再對矩陣中所有元素的每一位進行單點查詢即可。
時間複雜度:\(o(ktlog^2n+klog^2n)\),其中 \(k\) 最大為 \(30\)。
注:要卡下常才能過 \(subtask\ 2\) 。
分數:\(60pt\) 。
【code #2】
(此**並非完美,還有至少三處優化,但過 \(subtask\ 2\) 足矣)
#include#include#include#define re register int
#define ll long long
using namespace std;
const int n=503,p=1e9+7;
int n,m,a,b,c,d,x,t,hyj,ans1,ans2,a[n][n];
inline void in(re &x)
struct qaq}
inline void change(re x1,re y1,re x2,re y2)
inline int ask(re x,re y)
return ans>0;//只要有一次1即可
}}tt[31];
int main()
for(re p=0;p<=30;++p)
for(re i=1;i<=n;++i)
for(re j=1;j<=m;++j)
if(tt[p].ask(i,j))a[i][j]|=(1《考慮如何優化 \(solution\ \#2\) 。
依舊是對於每個二進位制位單獨處理。
想想「或」還有沒有什麼其他的性質?
單次覆蓋性!(其實就是自己瞎取的乙個名字)
現在只看乙個元素,只要有一次操作中對 \(1\) 取了 \(or\),那麼以後不管和誰 \(or\),都依然是 \(1\)。換句話說,將操作中 \(x\) 為 \(1\) 的二進位制位視作一張大小為 \((x_2-x_1)*(y_2-y_1)\) 的布,無論有多少張布覆蓋了這個元素,且無論覆蓋了多少次,只要有其中一次蓋住了它,那麼它最終就一定屬於被蓋住的部分(即對應的二進位制位為 \(1\))。
因此,如果我們在一次操作中覆蓋了某個元素,那麼下一次列舉到這裡時就可以直接跳過它(或者說直接刪掉該元素),易知每乙個元素都只會被覆蓋一次,因此對於乙個二進位制位下的矩陣覆蓋總複雜度為 \(o(n^2)\) 。
其中「刪除元素」可以用並查集實現:對於每一行都開乙個並查集表示該行中所有列的覆蓋情況,當 \((i,j)\) 被覆蓋後,把 \((i,j)\) 與 \((i,j+1)\) 所在的聯通塊連起來,然後再列舉該行下乙個聯通塊(即 \((i,j+1)\) 所在連通塊),大致**如下:
/*從x1到x2列舉i*/
for(re j=find(y1);j<=y2;j=find(j+1))
(i,j)=1,merge(j,j+1);
當然,這樣子做只優化了對於列的列舉,行的列舉同理,稍作修改即可。
時間複雜度:\(o(kt+n^2\alpha^2(n)+kn^2)\),其中 \(k\) 最大為 \(30\)(乙個不需要卡常也能輕鬆 \(\text\) 的優秀演算法)。
分數:\(100pt\) 。
【code #3】#include#include#include#define re register int
#define ll long long
using namespace std;
const int n=503,p=1e9+7;
int n,m,a,b,c,d,x,t,hyj,ans1,ans2,a[n][n];
inline void in(re &x)
struct qaq//注意初始化要列舉到n+1
inline int find(re x)
}f[n];
inline void cl()
inline void change(re x1,re y1,re x2,re y2)
}}tt[31];
int main()
for(re p=0;p<=30;++p)
for(re i=1;i<=n;++i)
for(re j=1;j<=m;++j)
if(tt[p].b[i][j])a[i][j]|=(1《本題實際上是兩個知識點的糅合:
\((1).\) 按照二進位制位拆分單獨處理(見 【題解】\(\text\) 機房遊記
\(\text\))
\((2).\) 冰茶姬優化矩陣覆蓋的列舉(見 【雜文】隨心一記 小知識第二條)
\(code\ \#2\) 最初第 \(4\) 個點死活卡不過去,最後將結構體套結構體換成乙個單一的結構體就過了(沒想到結構體的呼叫對常數影響這麼大)。
加上樣例一共 \(12\) 個不同的 \(hyj\),\(12\) 個數字 \(11\) 個梗您知道幾個?
luogu U103720 薇爾莉特 題解
題麵點這裡 概括一下題目,就是維護矩陣or,要求最後整個矩陣的和,異或和 考慮對需要操作的值val valva l進行二進位制拆分,由於or對於乙個二進位制位上的操作是永久性的,那我們對每一位進行二維差分,最後做一遍二維字首和,對大於1 11的都當做1 11看,就結束了 include define...
題解 薇爾莉特的打字機
首先,這題需要處理字串,我們用 trie分析 先忽略刪除操作 拿樣例 1 舉個例子 首先把最開始的字串插入到樹中 然後薇爾莉特打了乙個字元 a 此時可以插入或者是不插入,就會有這樣的情況 不插入時,之前插入進去的字元均可以作為字串的結尾 假設之前插入了 x個字母,每乙個字母都可以作為串的結尾 現在插...
薇爾莉特的打字機題解
好題 就是我死活想不到,看題解後卻不得不佩服思路巧妙的題 我們將一種字串看做乙個點 每一次操作就是對所有存在的點進行拓展 不按,狀態不變 按下,狀態增加 這就是先按a後按b的例子。1.輸入乙個字母 所有子樹中沒有此字母的點會增加,所以我們用乙個陣列 f x 記載沒有此字母 x 的點數 之後,原來的所...