雅礼学习10.7
雅禮學習10.7
上午考試
各題狀況
全TM神仙題。。。
\(T1\) \(35\)分暴力
\(T2\) 我\(n=1\)的時候直接輸出了\(1\),連這個格子上是否有方塊都沒判,當時是感覺。。。難道這個部分分也會卡么
結果出題人就這么卡了。。就一分都沒有了
太毒瘤了。
\(T3\) 成功騙\(8\)分
做了一段時間之后去做牛客網的來著。
跟人要了份暴力
然后我TM。。從紫名變成灰名了????
題目及考場代碼
T1
/** 暴力思路:從初始位置開始進行bfs*/ #include<queue> #include<cstdio> #include<algorithm>const int N=100001; int n,k,m,s,a[N],dis[N]; bool ban[N]; std::queue<int> que;void add(int x,int y) {if(x+y<=n && !ban[x+y]){que.push(x+y);ban[x+y]=true;dis[x+y]=dis[x]+1;}if(x-y>0 && ban[x-y]==false){que.push(x-y);ban[x-y]=true;dis[x-y]=dis[x]+1;} }void solve() {que.push(s);ban[s]=true;while(!que.empty()){int u=que.front();for(int i=1;i<=k;++i){if(!(k&1) && i&1)add(u,i);if(k&1 && !(i&1))add(u,i);}que.pop();} }int main() {freopen("reverse.in","r",stdin); freopen("reverse.out","w",stdout);scanf("%d%d%d%d",&n,&k,&m,&s);for(int x,i=1;i<=m;++i){scanf("%d",&x);ban[x]=true;}if(k==1){for(int i=1;i<=n;i++)if(i==s)printf("0 ");else printf("-1 ");goto E;}solve();for(int i=1;i<=n;++i){if(dis[i]==0)if(i==s)printf("0 ");elseprintf("-1 ");else printf("%d ",dis[i]);} E: fclose(stdin),fclose(stdout);return 0; }T2
#include <cstdio>inline int read() {int n=0,w=1;register char c=getchar();while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();return n*w; }const int N=1e5+1; int n,a[N],b[N];int main() {freopen("silhouette.in","r",stdin);freopen("silhouette.out","w",stdout);n=read();for(int i=1;i<=n;++i)a[i]=read();for(int i=1;i<=n;++i)b[i]=read();if(n==1){printf("1");goto E;} E: fclose(stdin);fclose(stdout);return 0; }T3
/**/ #include <cstdio>int n,p; inline int ksm(long long n,int b) {long long res=1;for(;b;b>>=1,n=n*n%p)if(b&1)res=res*n%p;return res; }int main() {freopen("seat.in","r",stdin);freopen("seat.out","w",stdout);scanf("%d%d",&n,&p);if(n==1)printf("1");elseif(n==2){int o=ksm(2,p-2);printf("%d %d\n",o,o);printf("%d %d\n",o,o);return 0;}else printf("19260817");fclose(stdin),fclose(stdout);return 0; }正解及代碼
T1
考慮當前在每個點時可以通過一次翻轉轉移到哪些點,直接\(BFS\)即可算出每個點的所需步數。然而邊數會達到\(O(n^2)\)級別。
可以發現轉移到的點一定是一段區間內的奇數或者偶數點,于是一種簡單的優化方法是在\(BFS\)時開兩個\(set\)維護當前有哪些奇數點和偶數點還未被\(BFS\)到,轉移時直接在\(set\)上\(lowerbound\),就不會訪問已經\(BFS\)到過的點了
\(O(n\log n)\)
#include <bits/stdc++.h>#define For(i, j, k) for (int i = j; i <= k; i++)using namespace std;const int N = 1e5 + 10;int n, K, m, S; int dis[N]; bool ban[N]; set<int> odd, even;void BFS() {memset(dis, -1, sizeof dis);queue<int> q;dis[S] = 0, q.push(S);while (!q.empty()) {int o = q.front(); q.pop();int L = max(1, o - K + 1), R = min(n, o + K - 1);L = L + (L + K - 1) - o, R = R + (R - K + 1) - o;set<int> &p = L & 1 ? odd : even;for (auto i = p.lower_bound(L); i != p.end() && *i <= R; p.erase(i++))dis[*i] = dis[o] + 1, q.push(*i);} }int main() {freopen("reverse.in", "r", stdin);freopen("reverse.out", "w", stdout);scanf("%d%d%d%d", &n, &K, &m, &S);For(i, 1, m) {int x;scanf("%d", &x);ban[x] = true;}For(i, 1, n) if (!ban[i] && i != S) i & 1 ? odd.insert(i) : even.insert(i);BFS();For(i, 1, n) printf("%d%c", dis[i], i == n ? '\n' : ' ');return 0; }T2
題目轉化:
實際是在問下面的方程組有多少非負整數解:
\[ \forall i\in [1,n],\max_{j=1}^n x_{i,j}=A_i,\max_{j=1}^n x_(j,i)=B_i \]
那么不妨先將\(A,B\)排序,顯然這樣不會有什么影響。如果\(A_\max\ne B_\max\)那么答案是\(0\),否則一定有解
顯然有\(x_{i,j}\le \min(A_i,B_j)\),從大到小枚舉每個\(A,B\)中出現的值\(s\),每次確定所有\(\min(A_i,B_j)=s\)的位置的值
考慮容斥,設\(f(i)\)為至少有\(i\)行的限制不滿足條件時的方案(需要保證每一列都滿足條件),那么有
\[ f(i)={a\choose i}(s^i\times ((s+1)^{a-i}-s^{a-i}))^b方案數就是\sum_{i=0}^a(-1)^i f(i) \]
方案數就是$\sum_{i=0}^a(-1)^i f(i)$加上快速冪取逆元,總復雜度\(O(n\log n)\)
#include <bits/stdc++.h>#define For(i, j, k) for (int i = j; i <= k; i++) #define Forr(i, j, k) for (int i = j; i >= k; i--)using namespace std;const int N = 5e5 + 10; const int Mod = 1e9 + 7;int Pow(int x, long long e) {int ret = 1;while (e) {if (e & 1) ret = 1ll * ret * x % Mod;x = 1ll * x * x % Mod;e >>= 1;}return ret; }int fac[N], rfac[N];int work(int a, int b, int la, int lb, int w) {int ans = 0;For(i, 0, a) {int s = Pow(Pow(w + 1, la + a - i) - Pow(w, la + a - i) + Mod, b);s = 1ll * s * Pow(w + 1, 1ll * lb * (a - i)) % Mod * Pow(w, 1ll * (b + lb) * i) % Mod;s = 1ll * s * rfac[i] % Mod * rfac[a - i] % Mod;if (i & 1) ans = (ans - s + Mod) % Mod; else ans = (ans + s) % Mod;}return 1ll * ans * fac[a] % Mod; }int A[N], B[N], num[N << 1]; int n, c;int main() {freopen("silhouette.in", "r", stdin);freopen("silhouette.out", "w", stdout);scanf("%d", &n);For(i, 1, n) scanf("%d", &A[i]), num[++c] = A[i];For(i, 1, n) scanf("%d", &B[i]), num[++c] = B[i];fac[0] = 1;For(i, 1, n) fac[i] = 1ll * fac[i - 1] * i % Mod;rfac[n] = Pow(fac[n], Mod - 2);Forr(i, n, 1) rfac[i - 1] = 1ll * rfac[i] * i % Mod;sort(A + 1, A + n + 1, greater<int>());sort(B + 1, B + n + 1, greater<int>());sort(num + 1, num + c + 1, greater<int>());c = unique(num + 1, num + c + 1) - num - 1;int la = 0, lb = 0;int ans = 1;For(i, 1, c) {int ra = la, rb = lb;while (ra < n && A[ra + 1] == num[i]) ++ra;while (rb < n && B[rb + 1] == num[i]) ++rb;ans = 1ll * ans * work(ra - la, rb - lb, la, lb, num[i]) % Mod;if (ans == 0) break;la = ra, lb = rb;}printf("%d\n", ans);return 0; }T3
一個結論是,對于任意一個人,他坐下時離最近的人的距離是固定的,不隨前面的人的決策而改變。這樣我們可以將所有人按坐下時的距離分成若干層。另一個結論是,無論之前每一層如何決策,輪到這一層時逬空區間的長度構成的可重集也是固定的。
對于最后一層特殊處理,接下來均默認不是最后一層。對于之前的每一層,考慮在哪些空
區間中央坐下可使得距離最大,其中可能會有一些長度為奇數的區間和一些長度為偶數的區間,而對于每個人來說,坐在任意一個奇數的區間的中央的概率都是相等的,偶數同理。
那么我們只需要知道,每個人有多大的概率坐在一個奇或偶數區間的中央。考慮\(dp[i][j]\)表示這一層已經坐下\(i\)個人之后,還有\(j\)個長度為偶數的區間的概率,轉移只需考慮當前這個人坐了哪類區間即可。\(dp\)之后容易算出之前要求的概率。
區間長度為奇數時位置是固定的,考慮區間長度為偶數的情況,此時會出現兩個位置可
供選擇,但無論選擇哪一個,都會將區間劃分成長度為\(\frac{n}{2}\)和\(\frac{n}{2}-1\)的兩段。因此這兩種選擇具有對稱性,我們只需要假定選擇其中的一個,算出這種情況下之后的層的答案,即可對稱地推出另一種情況下的答案。
瓶頸在利用對稱性推答案的地方,復雜度為\(O(n^2\log n)\)
下午講課
例1
\(Berland\)要舉行\(n\)次錦標賽,第一次只有一個人,之后每一次會新加入一個人。錦標賽中有\(k\)種運動項目,每個人在這\(k\)種項目上都有一個能力值,每次會選擇任意兩個還未被淘汰的人進行某個項目的比賽,能力值高的人勝出,輸的人被淘汰,直至只剩下一個人成為冠
軍。
給出每個人每個項目的能力值,保證它們兩兩不同,求每次錦標賽有多少人可能成為冠軍。
\(n\le 5e4,k\le 10\)
解:
只要選手\(a\)在某個項目上比選手\(b\)強,\(a\)就可以淘汰\(b\),我們可以連一條\(a\)到\(b\)的邊。
對整個圖求強連通分量,縮點后一定會形成一個競賽圖(任意兩點之間有有向邊相連),拓撲序最靠前的分量中的所有點都可能成為冠軍。
每加入一個點時,我們可能需要合并拓撲序在一段區間內強連通 分量。用\(set\)按拓撲序維護每個強連通分量,對每個分量記錄它的大 小,以及在每個項目上的最大和最小能力值,就可以直接在\(set\)上二分找到需要合并的區間。 最多只會合并n?1次,\(O(nk\log n)\)
例2
有\(X+Y+Z\)個人,第\(i\)個人有\(A_i\)個金幣,\(B_i\)個銀幣,\(C_i\)個銅幣。 選出\(X\)個人獲得其金幣,選出\(Y\)個人獲得其銀幣,選出\(Z\)個人獲得其銅幣。
在不重復選某個人的前提下,最大化獲得的幣的總數。\(X+Y+Z\le 10^5\)
解:
不妨先令\(A_i=A_i?C_i,B_i=B_i?C_i\),問題變為選出\(X\)個人獲得其金幣,選出\(Y\)個人獲得其銀幣,再將答案加上\(\sum C_i\). 按\(A_i\)從大到小排序,枚舉選出的\(X\)個人中\(A_i\)最小的人,顯然這個人之前的人要么在選出的\(X\)個人中,要么在選出的\(Y\)個人中。 那么我們只要對每個位置\(i\),\(X\le i\le X+Y\)計算兩個信息:\(i\)之前\(A_i?B_i\)最大的\(X\)個人的\(A_i ?B_i\)的和,\(i\)之后\(B_i\)最小的\(Z\)個人的\(B_i\)之和 。 于是我們只需要從前往后掃一遍,用小根堆維護當前\(A_i?B_i\)最大的\(X\)個人,每加入一個人與堆頂比較;再從后往前算第二個信息即可。\(O(n\log n)\)
例3
有一個\(1\)到\(n\)的排列\(p\),我們稱一個子區間是好的,當且僅當這個子區間內的值構成了連續的一段(例如對于排列\(\{1,3,2\},[1,1], [2,2], [3,3], [2,3],[1,3]\)是好的區間)。
\(q\)次詢問,每次詢問\(L,R\), 求有多少\(L\le l\le r\le R\),滿足\([l,r]\)是好的區間。
\(n,q\le 1.2×10^5,TL=7s\)
解:
一段區間是好的,當且僅當\((\max?\min)?(r?l)=0\). 考慮從小到大枚舉\(r\),用線段樹對每個l維護上式的值。維護兩個棧,分別保存\(l\)在每段區間中時\(max\)和\(min\)的值,維護棧的同時在線段樹上修改對應區間的值。
線段樹上需要維護最小值以及最小值數量,若最小值為\(0\)(實際上全局最小值一定是\(0\)),則所有最小值的位置對于當前的\(r\)都是合法的左端點。于是線段樹上存一個“所有最小值的位置的答案”的加標記,并維護區間答案之和。
將詢問離線按\(R\)排序,我們就可以在\(R\)處查詢區間答案之和來回答詢問。
\(O(n\log n)\)
例4
有\(10^5\)個初始為空的集合。對于一個集合,若其中沒有元素\(x\)滿足\(x\gt 0\),或沒有元素\(x\)滿足\(x\lt 0\),則定義其優美度為\(0\);否則其優美度等于最小的正的元素減去其最大的負的元素。
\(q\)次操作,每次操作要么向一個區間內的集合加入某個元素,要么詢問一個區間內集合的優美度之和。\(q\le 5\times 10^4,TL=4s\)
解:
對于正的和負的元素分開考慮。如果每個集合都有正的和負的元素,那么這道題就是要支持區間取\(\min\)以及區間求和。
這可以對每個位置維護最大值\(\max\)和次大值\(\sec\),以及最大值數量和一個取\(\min\)的標記。如果現在要對\(x\)取\(\min\),分情況討論:
- 若\(x\ge \max\)直接忽略該操作
- 若\(sec\lt x\lt \max\),更新最大值,根據記錄的最大值數量快速算出新的區間和。
- 若\(sec\le x\),直接暴力下傳。(可用勢能分析證明暴力下傳次數為\(O(q\log n)\)級別)。
回到本題,唯一要注意的是如果一個集合還沒有正的或負的元素就不要計入它的信息,當一個集合第一次加入正的或負的元素時,可以暴力處理。\(O((n+q)\log n)\)
從相識到現在,從冷淡到關懷,從拒絕到依賴,從陌生到相愛,從深信到疑猜,從疼愛到傷害,從炫爛到蒼白,從廝守到分開,從感動到感慨,從體諒到責怪,從期待到無奈,從狂喜到悲哀
當你嘗盡人情冷暖,當你決定為了你的理想燃燒,捫心自問,生活的壓力與生命的尊嚴哪一個更重要?
想得卻不可得,你奈人生何。
該舍的舍不得,只顧著與俗事糾葛。
等你發現妥協是賊了,它早已偷光你的選擇。
名利不過是一場高燒,遺憾是緊跟著的、好不了的咳。
我可以原諒,卻無法阻擋。
獨自在夜里感傷。
是年少輕狂,卻故作滄桑。
誰給你勇氣去偽裝。
丟棄的理想像極了一個巴掌。
每當你記起一句就挨一個耳光。
然后就開始反省著、痛恨著自己的臟。
轉載于:https://www.cnblogs.com/kuaileyongheng/p/9774574.html
總結
- 上一篇: JavaScript 函数 伪数组 a
- 下一篇: appium 的 android sdk