全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)\)
从相识到如今,从冷淡到关怀,从拒绝到依赖,从陌生到相爱,从深信到疑猜,从疼爱到伤害,从炫烂到苍白,从厮守到分开,从感动到感慨,从体谅到责怪,从期待到无奈,从狂喜到悲哀
当你尝尽人情冷暖,当你决定为了你的理想燃烧,扪心自问,生活的压力与生命的尊严哪个更重要?
想得却不可得,你奈人生何。 该舍的舍不得,只顾着与俗事纠葛。 等你发现妥协是贼了,它早已偷光你的选择。 名利不过是一场高烧,遗憾是紧跟著的、好不了的咳。 我能够原谅,却没法阻挡。 独自在夜里感伤。 是年少轻狂,却故做沧桑。 谁给你勇气去假装。 丢弃的理想像极了一个巴掌。 每当你记起一句就挨一个耳光。 而后就开始检讨着、痛恨着本身的脏。