10.4 - 11.4 改題紀要
還是決定寫一起。
等寫完了就會把問號改了
衝刺CSP聯訓模擬2
以後見了計數先想容斥!!!
-
T1 擠壓
按位拆開,每兩位一塊考慮,列舉每個數是否選,\(O(nlog^2n)\) 的。
-
T2 工地難題
考慮恰好比較難做,先字首和變成至少。
發現 \(0\) 個數一定,其將序列分成若干小段,每段有若干 \(1\)。
相當於是 \(\sum x_i = k(x_i<k)\) 的形式,直接容斥即可。
-
T3 星空遺蹟
首先發現點顯然的性質。
- 連續一段的長度無意義。
- 如果一段中兩端(如果只有一端就是一端)都是能贏他的,就可以刪掉這一段。
- 依次刪掉後一定只剩下一段相同的。
由此可以有單調棧寫法,保證任意一個非棧頂元素一定會輸給其下一個(也可寫逆天並查集,但好像不太好擴充套件到正解)。
考慮修改。
對於每個元素,維護棧在加入它後的大小,設其為 \(f_i\):
\[f_i=\begin{cases} f_i+1 & s_i<s_{i-1}\vee i=1\\f_i & s_i=s_{i-1}\\\max(f_i-1,1) & s_i>s_{i-1}\end{cases} \]最後的答案就是最後一個為 \(1\) 的位置
發現這個 \(max\) 很難整,考慮直接去掉,發現其相當於是連續下降,容易證明有一個好的性質:只有最小值和最後一個 \(1\) 一一對應。
但其實也不用特意找最後一個,考慮升降是對稱的,所以棧大小相同的元素一定是一樣的。
維護單點改,字首和的區間 \(min\),可以直接上線段樹上二分,也可以字首和變成區間加,區間 \(min\)。
-
T4 紐帶
析合樹上 \(dp\),這是我現在能改的???
多校A層衝刺NOIP2024模擬賽03
兩籤一原(雖然原不是都做過,但是也是籤)。
-
T4 量子隧穿問題(experiment):
挺好的題,@wang54321 不要因為你實現屎就說題屎。
發現記錄有沒有貓的方案數比較麻煩,這裡我們記錄機率。
顯然基環樹,先考慮只是一棵樹怎麼做,考慮設 \(dp_{i,j}\) 表示第 \(i\) 時刻 \(j\) 的機率,依次列舉邊轉移即可,轉移就是考慮貓是否轉移。
發現有環就掛了,因為考慮若 \(p(p<1)\) 的機率有貓,\(1-p\) 的機率沒貓,在環上轉移一圈時每一個都對 \(p\) 有依賴,在最後轉移到起點時就會將依賴有貓和依賴沒貓的結合,導致錯誤。
可能可以透過逆天小技巧進行最後一步轉移,但這裡介紹比較簡單的欽定。
考慮題解做法,欽定第一條邊的左右端點來斷邊,欽定其在跳環前是 \(1,1\)、\(0,1\)、\(1,0\)、\(0,0\) 分討,最後乘上其機率即可。
發現 \(0,1\) 和 \(1,0\) 在跳第一次之後一模一樣,可以只分討三種。
考慮有衝突的關鍵是依賴有貓和依賴沒貓的機率的衝突,只欽定環上第一次跳的點即可,只用分討兩種。
csp-s模擬9
accoders 的模擬賽出過了,於是有了這個代替,不是我們 csp/noip 模擬賽出兩道 UNR 是吧。
-
T1 鄰面合併
發現 \(m\) 很小,直接用
vector
狀壓即可。也可以壓二進位制,發現對於每一層那些地方會有矩形是固定的,狀態只是分割點,可以 \(2^8\) 壓起來。
-
T2 光線追蹤
考慮橫線和豎線互不影響,分開考慮。
每條線對應一個斜率範圍,離散化後就是區間推 \(\min\),單點查。
注意邊界。
-
T3 百鴿籠
好題,單開了一篇 this
-
T4 滑稽樹下你和我
計算幾何,沒準會改。真的改了。
首先需要知道怎麼求點到線段距離,考慮先點積判斷是否垂足線上段上,用叉積的模除以底來求垂線長。
可以看 this
二分答案。
首先有結論,如果在兩個點在兩條線段一端時滿足,在另一端時也滿足,則一定有滿足要求的一種移動。
證明就是發現將一條線段放平,另一條一定時單調的。
於是在特殊性質時可以直接列舉兩個點來轉移。
考慮在非特殊性質時,會有一個點在一條邊上等另一個,考慮將邊變成點,由於有結論,在最優時一定是等的點在離另一個點最近的點上,直到另一個點到達是其依然在最近的點上。
形如:
形如(圖不咋地,湊合吧)
考慮將邊變成點,其和其他點的距離就是點到直線距離,可以直接轉移,但有小常數的做法,設 \(dp_{i,j}\) 表示一個點在 \(i\) 邊上,另一個在 \(j\) 點上是否可行,發現其也可以表示所有情況。
放個程式碼吧
#include<bits/stdc++.h> using namespace std; using llt=long long; using llf=long double; using ull=unsigned long long; #define endl '\n' #ifdef LOCAL FILE *InFile=freopen("in_out/in.in","r",stdin),*OutFile=freopen("in_out/out.out","w",stdout); #else FILE *InFile=stdin,*OutFile=stdout; #endif const double Eps=1e-8; int Cmp(double a,double b){return fabs(a-b)<Eps?0:(a<b?-1:1);} struct Pnt{double x,y; Pnt(){} Pnt(double a,double b):x(a),y(b){}}; double Dis(const Pnt &a,const Pnt &b){return sqrtl((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));} struct Vec{ double x,y; Vec(){} Vec(double a,double b):x(a),y(b){} Vec(const Pnt &a,const Pnt &b):x(b.x-a.x),y(b.y-a.y){} Vec &operator+=(const Vec &b){x+=b.x,y+=b.y; return *this;} Vec operator-()const{return Vec(-x,-y);} Vec &operator-=(const Vec &b){*this+=-b; return *this;} Vec operator+(const Vec &b)const{Vec ans=*this; return ans+=b;} Vec operator-(const Vec &b)const{Vec ans=*this; return ans-=b;} Vec &operator*=(double k){x*=k,y*=k; return *this;} friend Vec operator*(double k,Vec a){a*=k; return a;} Vec operator*(double k)const{return k*(*this);} double operator*(const Vec &b)const{return x*b.x+y*b.y;} double operator^(const Vec &b)const{return x*b.y-y*b.x;} }; struct Lin{ Pnt a,b; Lin(){} Lin(const Pnt &a1,const Pnt &a2):a(a1),b(a2){} Lin(double a1,double a2,double a3,double a4):a(a1,a2),b(a3,a4){} operator Vec()const{return Vec(a,b);} operator double()const{return Dis(a,b);} }; double Dis(const Pnt &p,const Lin &l){ if(Vec(l.a,p)*Vec(l.a,l.b)<0) return Dis(p,l.a); if(Vec(l.b,p)*Vec(l.b,l.a)<0) return Dis(p,l.b); return abs(Vec(l.a,l.b)^Vec(l.a,p))/double(l); } const int N=1003; struct Gph{ int hd[N],to[N<<1],nt[N<<1],wt[N<<1],tot=1; void Add(int u,int v,int w){wt[++tot]=w,to[tot]=v,nt[tot]=hd[u],hd[u]=tot;} void ADD(int u,int v,int w){Add(u,v,w),Add(v,u,w);} #define For_to(i,u,v,g) for(int i=g.hd[u],v=g.to[i];i;i=g.nt[i],v=g.to[i]) }g; int n,sa,sb,cd[N]; Pnt cp[N]; Lin cl[N]; bool dp[N][N]; bool Chk(double lim){ memset(dp,0,sizeof(dp)); queue<pair<int,int>> que; auto Lim=[&lim](double a){return Cmp(a,lim)<=0;}; For_to(i,sa,v,g) if(Lim(Dis(cp[sb],cl[g.wt[i]]))) dp[sb][g.wt[i]]=1,que.emplace(sb,i); For_to(i,sb,v,g) if(Lim(Dis(cp[sa],cl[g.wt[i]]))) dp[sa][g.wt[i]]=1,que.emplace(sa,i); while(!que.empty()){ auto tmp=que.front(); que.pop(); int p=tmp.first,l=tmp.second; if(cd[p]==1&&((cd[g.to[l]]==1&&Lim(Dis(cp[p],cp[g.to[l]])))||(cd[g.to[l^1]]==1&&Lim(Dis(cp[p],cp[g.to[l^1]]))))) return 1; For_to(i,p,v,g){ if(!dp[v][g.wt[l]]&&Lim(Dis(cp[v],cl[g.wt[l]]))) dp[v][g.wt[l]]=1,que.emplace(v,l); int np=g.to[l],nl=g.wt[i]; if(!dp[np][nl]&&Lim(Dis(cp[np],cl[nl]))) dp[np][nl]=1,que.emplace(np,i); np=g.to[l^1]; if(!dp[np][nl]&&Lim(Dis(cp[np],cl[nl]))) dp[np][nl]=1,que.emplace(np,i); } } return 0; } int main(){ ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr); cin>>n>>sa>>sb; cout<<fixed<<setprecision(10); for(int i=1;i<=n;++i) cin>>cp[i].x>>cp[i].y; for(int i=1;i<n;++i){int a,b; cin>>a>>b; cl[i]=Lin(cp[a],cp[b]),g.ADD(a,b,i),++cd[a],++cd[b];} double l=Dis(cp[sa],cp[sb]),r=2e6; for(int i=1;i<=40;++i){ double mid=(l+r)/2; if(Chk(mid)) r=mid; else l=mid; } cout<<l; }
多校A層衝刺NOIP2024模擬賽04
-
T4 表示式:
首先觀察性質,發現除了暴力以外所有模數都可以分解成較小的互質數相乘。
直接線段樹維護值域套 CRT 合併即可。
多校A層衝刺NOIP2024模擬賽06
-
T3 一個真實的故事:
首先發現值域很小,考慮直接用線段樹維護,合併的時候暴力將左邊最靠右和右邊最靠左的不同 \(k\) 個歸併掃描線即可。
有和值域無關的做法,可以看 this(
馬上就寫寫了) -
T4 異或區間(xor):
經典結論:對於每個值所管轄的 \(max\) 區間中左邊和右邊較小的一邊的和是 \(n\log n\) 級的。
證明可以考慮笛卡爾樹。
於是有兩種做法,一是用可持久化 trie 每次遍歷較小區間,一是建笛卡爾樹跑啟發式。
csp-s模擬11
不是非多校的題這麼難。(斷句:不是、非多校的題這麼難?)
-
T3 暴雨:
考慮對於兩邊都漏的顯然是不好整,考慮列舉前 \(k\) 大來作為最大的分割左右(比它大的直接剷掉)。
考慮只有一邊要列舉,設 \(dp_{i,j,k}\) 表示當前在 \(i\),挖了 \(k\) 下,前面最高的是 \(k\),暴力轉移是 \(O(n^2k)\) 的,但是發現最高的只有可能是前 \(k\) 大,於是可以做到 \(O(nk^2)\),總複雜度 \(O(nk^3)\)。
有個簡單的實現是將第三維保留 \(n\) 的大小,將第一維滾掉最佳化空間,這樣不用下標的重編號。
當我們寫出 \(dp\) 後,我們發現列舉前 \(k\) 大純唐,可以直接將字首字尾用一個拼起來轉移,不用每次重做一遍,複雜度變為 \(nk^2\)。
-
T2 AVL 樹:
根據前序遍歷或中序遍歷貪心即可,因為樹高是 \(\log\) 的每次暴力跳爹來更新右子樹最少選的個數,細節不少。
-
T4 置換:
首先是置換環經典結論,其次數等於 \(\operatorname{lcm\{環長\}}\)。
考慮 \(dp\),設 \(dp_{i,j}\) 表示以 \(i\) 為環長,\(j\) 為 \(\operatorname{lcm}\) 的機率,列舉長度和個數轉移即可,用方案數的乘上 \(\operatorname{lcm}^2\),注意在最後除掉 \(n!\)
用 \(map\) 輕鬆 60pts,考慮最佳化。
發現對於一個 \(\ge \sqrt n\) 的因子,其最大次數是一,先提出來,\(dp\) 完放回即可。
-
T5 傳統題:
是個好題,先放這提醒我改。
多校A層衝刺NOIP2024模擬賽07
-
T3 距離(distance):
首先用樹形 \(dp\) 經典結論,每次合併子樹用 \(sz_u\times sz_v\)(這裡 \(sz_u\) 是已經合併的子樹大小)總複雜度 \(n^2\),證明考慮每個點對只會在 \(lca\) 處有 \(1\) 的貢獻。
但正解和這個沒關係。
考慮 \(\min\max\) 容斥,將 \(\max{|x_a-x_b|,|y_a-y_b|}\) 也就是切比雪夫距離轉曼哈頓距離,問題就變成了求解四個不相關的絕對值。
暴力可以樹狀陣列套 dsu on tree,用巴雷特約減可以卡過 accoder,過不了學校 oj。
可以直接用值域線段數,類似 cdq 統計左區間對右區間的貢獻,線段樹合併即可。
-
T4 團隊選拔(selection):
考慮一個 \(n^3\) 做法:設 \(dp_{i,j}\) 表示字首是 \(i\),\(\gcd\) 是 \(j\) 的方案數,最後列舉一遍 \(a_i\) 和 \(\gcd\) 轉移即可。
發現顯然結論,每個數為起點的所有區間的 \(\gcd\) 的種類數是 \(\log\) 級別的,\(n\) 個數就是 \(n\log n\) 級別的。
於是我們用棧處理出每個數前字尾區間的 \(\gcd\) 種類所對應的左右端點區間,具體的就是每次加入時將當前棧遍歷一遍,和這個數取 \(\gcd\) 後合併相同區間,顯然任意時刻棧的大小是 \(\log\) 級的。
然後列舉每個 \(\gcd\),總共有用的轉移點一共就 \(n\log n\) 個,對於沒有轉移點的直接區間推平,用線段樹維護,答案是區間加,差分即可。
多校A層衝刺NOIP2024模擬賽08
-
T3 戰場模擬器 (simulator) :
考慮每個人只會死一次,盾也只有一次貢獻,用線段樹維護區間 \(min\) 暴力改即可(也可以用分塊微調快長)。
-
T4 點亮 (light) :
首先經過轉換題意,發現其每個聯通塊都是一個重邊加上一些邊組成的樹,聯通塊個數就是重邊個數。
容斥,設 \(g(x)\) 表示欽定(至少) \(x\) 條重邊的方案數。
不太顯然的是:
\[g(x)=\frac{n!}{2^x(n-2i)!}\prod_{j=1}^x \frac{1}{\binom{n}{2}-\binom{n-2j}{2}} \]解釋就是在 \(\prod\) 後面的部分是欽定選的邊求機率,考慮從大到小按邊權加邊,將 \(j\) 理解為從 \(x\) 到 \(1\) 可能好理解一點,\(\prod\) 前面的部分是在選這 \(x\) 條邊(式子約了一個 \(i!\))。
csp-s模擬12
-
T3 小 y 的數論
先考慮全域性,這個是簡單的,顯然取長剖後的前 \(k\) 長鏈即可。
考慮區間,顯然可以直接推到虛樹上,考慮合併,對於 \(k=2\) 時其有優秀性質:直徑可以合併,類似的,我們猜測 \(k>2\) 時也有可以合併的性質,其實確實有,證明也比較顯然,考慮每次必然取的時長鏈端點,而端點又一定是之前的端點。
於是有了好做法,ST 表維護,合併時對於左右各 \(k\) 個點建虛樹,長剖求答案,查詢時依然這樣合併。
稍微分析一下複雜度就知道,根本過不去!
考慮分塊來平衡複雜度,每 \(K\) 個分一塊,塊間維護 ST 表,最後將散塊暴力合併即可。
-
T4 小j 的組合
是籤,想不到吧。
考慮複製一個點等價於讓一個點的經過次數 \(+1\),考慮樹上的哈密頓路就是 \(dfs\) 一遍,發現只有一條鏈可以只經過一遍,於是就是找直徑板子。
多校A層衝刺NOIP2024模擬賽09
-
T1 排列最小生成樹
不是,\(n\sqrt n\log n\) 過不去 \(5\times 10^4\) 啊啊啊!
首先發現邊權 \(> n\) 的點對一定不會選,因為顯然從 \(1\) 連到 \(n\) 連一條鏈就沒有邊權 \(>n\)。
發現兩個點能連當且僅當 \(|i-j|<=\sqrt n\) 或 \(|p_i-p_j|<=\sqrt n\),其一共只有 \(n\sqrt n\) 對,最後桶排跑最小生成樹即可。
-
T4 區間 (interval)
首先離線,將詢問離線到右端點,掃描線。
考慮如何維護第一個限制,用單調棧即可。
考慮第二個限制,直接上線段樹,於是變成了區間歷史和板子。
多校A層衝刺NOIP2024模擬賽10
-
T3 TG393. 列表
this(罕見的提前寫好了)
後面的基本都是逆天區分度,不寫了。
也許會把幾個不太逆天的單改一下。
好像還有一道『T5 傳統題』沒改,別急,一定會改的。
完結撒花!!正好一個月
P