全TM神仙題。。。c++
\(T1\) \(35\)分暴力學習
\(T2\) 我\(n=1\)的時候直接輸出了\(1\),連這個格子上是否有方塊都沒判,當時是感受。。。難道這個部分分也會卡麼優化
結果出題人就這麼卡了。。就一分都沒有了spa
太毒瘤了。3d
\(T3\) 成功騙\(8\)分code
作了一段時間以後去作牛客網的來着。
跟人要了份暴力
而後我TM。。從紫名變成灰名了????blog
/* * 暴力思路:從初始位置開始進行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 "); else printf("-1 "); else printf("%d ",dis[i]); } E: fclose(stdin),fclose(stdout); return 0; }
#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; }
/* */ #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"); else if(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; }
考慮當前在每一個點時能夠經過一次翻轉轉移到哪些點,直接\(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; }
題目轉化:get
實際是在問下面的方程組有多少非負整數解:
\[ \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)={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; }
一個結論是,對於任意一我的,他坐下時離最近的人的距離是固定的,不隨前面的人的決策而改變。這樣咱們能夠將全部人按坐下時的距離分紅若干層。另外一個結論是,不管以前每一層如何決策,輪到這一層時逬空區間的長度構成的可重集也是固定的。
對於最後一層特殊處理,接下來均默認不是最後一層。對於以前的每一層,考慮在哪些空
區間中央坐下可以使得距離最大,其中可能會有一些長度爲奇數的區間和一些長度爲偶數的區間,而對於每一個人來講,坐在任意一個奇數的區間的中央的機率都是相等的,偶數同理。
那麼咱們只須要知道,每一個人有多大的機率坐在一個奇或偶數區間的中央。考慮\(dp[i][j]\)表示這一層已經坐下\(i\)我的以後,還有\(j\)個長度爲偶數的區間的機率,轉移只需考慮當前這我的坐了哪類區間便可。\(dp\)以後容易算出以前要求的機率。
區間長度爲奇數時位置是固定的,考慮區間長度爲偶數的狀況,此時會出現兩個位置可
供選擇,但不管選擇哪個,都會將區間劃分紅長度爲\(\frac{n}{2}\)和\(\frac{n}{2}-1\)的兩段。所以這兩種選擇具備對稱性,咱們只須要假定選擇其中的一個,算出這種狀況下以後的層的答案,便可對稱地推出另外一種狀況下的答案。
瓶頸在利用對稱性推答案的地方,複雜度爲\(O(n^2\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 = 1030; int Mod; int Pow(int x, int e) { int ret = 1; while (e) { if (e & 1) ret = ret * x % Mod; x = x * x % Mod; e >>= 1; } return ret; } void add(int &x, int y) { x += y; x = x >= Mod ? x - Mod : x; } int n; int dp[N][N], f[N][N], g[N][N]; bool vis[N]; int inv[N], cnt[N], odd[N], pos[N]; int main() { freopen("seat.in", "r", stdin); freopen("seat.out", "w", stdout); scanf("%d%d", &n, &Mod); For(i, 1, n) inv[i] = Pow(i, Mod - 2); vis[0] = vis[n + 1] = true; For(i, 1, n) { int pl = 0, pr = 0; For(j, 0, n) { int r = j + 1; while (!vis[r]) ++r; if (r - j > pr - pl) pl = j, pr = r; j = r - 1; } int mx = (pr - pl) / 2; cnt[mx]++; pos[i] = pl + mx; vis[pl + mx] = true; if ((pr - pl) % 2) odd[mx]++; } int sum = n; For(i, 1, n) if (cnt[i]) { int l = sum - cnt[i] + 1, r = sum; if (i == 1) { For(j, l, r) For(k, l, r) dp[j][pos[k]] = inv[cnt[i]]; } else { For(j, 0, cnt[i]) For(k, 0, odd[i]) f[j][k] = 0; f[0][odd[i]] = 1; int p = l + odd[i] - 1; For(j, 1, cnt[i]) { int oddw = 0, evenw = 0; For(k, 0, odd[i]) if (f[j - 1][k]) { int frac = (cnt[i] - j + 1) + k, w = 0; if (k) { w = f[j - 1][k] * k * 2 % Mod * inv[frac] % Mod; oddw = (oddw + w * inv[odd[i] * 2]) % Mod; add(f[j][k - 1], w); } if (cnt[i] - odd[i]) { w = f[j - 1][k] * (frac - 2 * k) % Mod * inv[frac] % Mod; evenw = (evenw + w * inv[cnt[i] - odd[i]]) % Mod; add(f[j][k], w); } } For(u, l, p) add(dp[l + j - 1][pos[u]], oddw), add(dp[l + j - 1][pos[u] + 1], oddw); For(u, p + 1, r) add(dp[l + j - 1][pos[u]], evenw); } For(j, l, p) { int L = pos[j] - i + 1, R = pos[j] + i; For(v, L, R) if (v != pos[j]) For(u, r + 1, n) { int s = v < pos[j] ? v + i + 1 : v - i, w = dp[u][v] * inv[2] % Mod; add(g[u][v], w), add(g[u][s], w); } For(v, L, R) For(u, r + 1, n) dp[u][v] = g[u][v], g[u][v] = 0; } } sum = l - 1; } for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) printf("%d%c", dp[i][j], j == n ? '\n' : ' '); return 0; }
\(Berland\)要舉行\(n\)次錦標賽,第一次只有一我的,以後每一次會新加入一我的。錦標賽中有\(k\)種運動項目,每一個人在這\(k\)種項目上都有一個能力值,每次會選擇任意兩個還未被淘汰的人進行某個項目的比賽,能力值高的人勝出,輸的人被淘汰,直至只剩下一我的成爲冠
軍。
給出每一個人每一個項目的能力值,保證它們兩兩不一樣,求每次錦標賽有多少人可能成爲冠軍。
\(n\le 5e4,k\le 10\)
解:
只要選手\(a\)在某個項目上比選手\(b\)強,\(a\)就能夠淘汰\(b\),咱們能夠連一條\(a\)到\(b\)的邊。
對整個圖求強連通份量,縮點後必定會造成一個競賽圖(任意兩點之間有有向邊相連),拓撲序最靠前的份量中的全部點均可能成爲冠軍。
每加入一個點時,咱們可能須要合併拓撲序在一段區間內強連通 份量。用\(set\)按拓撲序維護每一個強連通份量,對每一個份量記錄它的大 小,以及在每一個項目上的最大和最小能力值,就能夠直接在\(set\)上二分找到須要合併的區間。 最多隻會合並n−1次,\(O(nk\log n)\)
有\(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)\)
有一個\(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)\)
有\(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\),分狀況討論:
回到本題,惟一要注意的是若是一個集合尚未正的或負的元素就不要計入它的信息,當一個集合第一次加入正的或負的元素時,能夠暴力處理。\(O((n+q)\log n)\)
從相識到如今,從冷淡到關懷,從拒絕到依賴,從陌生到相愛,從深信到疑猜,從疼愛到傷害,從炫爛到蒼白,從廝守到分開,從感動到感慨,從體諒到責怪,從期待到無奈,從狂喜到悲哀
當你嚐盡人情冷暖,當你決定爲了你的理想燃燒,捫心自問,生活的壓力與生命的尊嚴哪個更重要?
想得卻不可得,你奈人生何。 該舍的捨不得,只顧着與俗事糾葛。 等你發現妥協是賊了,它早已偷光你的選擇。 名利不過是一場高燒,遺憾是緊跟著的、好不了的咳。 我能夠原諒,卻沒法阻擋。 獨自在夜裏感傷。 是年少輕狂,卻故做滄桑。 誰給你勇氣去假裝。 丟棄的理想像極了一個巴掌。 每當你記起一句就挨一個耳光。 而後就開始檢討着、痛恨着本身的髒。