ABC 做题笔记

news/2024/12/31 6:03:21/文章来源:https://www.cnblogs.com/kilomiles/p/18353100

1. [ABC313G] Redistribution of Piles

简要题意

\(n\) 个盘子和 \(1\) 个袋子。初始时,第 \(i\) 个盘子上有 \(a_i\) 块石头,袋子是空的。

有以下两种操作:

  • 对于每个盘子,如果当前盘子上有石头,那么从该盘子上移除一块石头。然后将所有移除掉的石头放进袋子。
  • 从袋子里拿出 \(n\) 块石头,并依次在每个盘子中放入 \(1\) 块。这种操作能够进行当且仅当此时袋子里有至少 \(n\) 块石头。

你可以按任意顺序进行任意多次操作,包括 \(0\) 次。求所有能够到达的局面数量,对 \(998244353\) 取模。

\(1 \leq n \leq 2 \times 10^5\)\(0 \leq a_i \leq 10^9\)

我们称一种局面为一种“状态”当且仅当这种局面中存在某个盘子为空。称一种局面与一种状态互相对应当且仅当从该状态出发,能够只经过操作 2 变成该局面。显然,一种局面对应的状态是唯一的(只需不断地从每个盘子中移除一块石头,直到某个盘子为空),但一种状态可能对应多个局面。因此答案即为所有状态对应的局面数量之和。

考虑如何求出一种状态对应的局面数量。显然每次操作会多出恰好 \(n\) 块石头和恰好一种局面。因此,如果总共有 \(\mathrm{sum}\) 块石头,且该状态有 \(x\) 块石头,则该状态会对应 \(\lfloor \dfrac{sum - x}{n} \rfloor + 1 = \lfloor \dfrac{sum - x + n}{n} \rfloor\) 种状态。

然而我们发现这道题的值域(单个盘子上的石子数)是 \(10^9\) 级别的,这说明虽然我们刚才的操作大大优化了复杂度,但现在仅连状态数都仍然不可接受。我们考虑将这些状态归类,定义一个状态的类别为这个状态中空盘子的数量,并设计一种算法快速地求出每一类包含的局面数量。事实上,若初始时将这些盘子按石子数升序排序,则每种状态中的空盘子一定集中在最左边,这种状态的类别即为最右端空盘子的编号。由此,对于一种类别,我们就能快速表示出它对应的状态。

现在我们只需考虑如何求出第 \(i\) 类状态包含的局面数。事实上,我们发现状态之间只能通过操作 1 转移。因此,如果我们找出了石子数最大的状态,该类别中的状态都可以被表示成一个非负整数 \(k\),表示保持左边的 \(i\) 个空盘子不变,右边 \(n - i\) 个非空的盘子上石子数量都减少 \(k\)。显然 \(k\) 的值域为 \([0, a_i - a_{i + 1})\),且其中的每个整数都会出现恰好 \(1\) 次。将上面的式子代入,我们得出第 \(i\) 类状态包含的局面数为

\[\sum_{k = 0}^{a_i - a_{i + 1}} \lfloor \dfrac{\mathrm{sum} - x + n + k(n - i)}{n} \rfloor \]

其中 \(\mathrm{sum}\) 表示总石子数,\(x\) 表示该类中石子数最大的状态中的石子数。

我们发现这个式子满足 floor-sum 算法的要求,因此只需调用 \(f(n - i, \mathrm{sum} - x + n, n, a_{i + 1} - a_i)\) 即可。时间复杂度为 \(\Theta(n \cdot \log V)\)

AC 代码
#include <algorithm>
#include <cstdio>
using namespace std;
const int N=200003,mod=998244353;
int n,a[N];
long long floor(long long a,long long b){return (a>=0)?(a/b):-((-a+b-1)/b);
}
int f(long long a,long long b,int c,int n){// calculate the sum of floor(a * i + b)/c (1 <= i <= n).int sa=(floor(a,c)%mod+mod)%mod,sb=(floor(b,c)%mod+mod)%mod;int ans=(((long long)(n+1)*n/2%mod*sa%mod+(long long)n*sb%mod)%mod+mod)%mod;a-=floor(a,c)*c,b-=floor(b,c)*c;if(n==0||a==0) return ans;return ((a*n+b)/c*n%mod-f(c,-b-1,a,(a*n+b)/c)+ans+mod)%mod;
}
int main(){
//	freopen("pile.in","r",stdin);
//	freopen("pile.out","w",stdout);int i,ans=0;long long sum=0;scanf("%d",&n);for(i=1;i<=n;i++)scanf("%d",&a[i]);sort(a+1,a+n+1);for(i=1;i<=n;i++) sum+=a[i];for(i=n;i>0;i--) a[i]-=a[1];for(i=1;i<=n;i++) sum-=a[i];for(i=1,ans=(sum/n+1)%mod;i<n;i++){ans=(ans+f(n-i,sum+n,n,a[i+1]-a[i]))%mod;sum+=(long long)(a[i+1]-a[i])*(n-i);}printf("%d",ans);
//	fclose(stdin);
//	fclose(stdout);return 0;
}

2. [ABC366F] Maximum Composition

简要题意

给定 \(n\) 个函数 \(f_i\),其中 \(f_i(x) = a_i x + b_i\)

对于所有元素互不相同的序列 \(p = (p_1, p_2, \cdots, p_k)\),其中所有元素均在 \([1, n]\) 中,求 \(f_{p_1}(f_{p_2}(\cdots(f_{p_k}(1))))\) 的最大值。

\(1 \leq n \leq 2 \times 10^5\)\(1 \leq k \leq \min\{ n, 10 \}\)\(1 \leq a_i, b_i \leq 50\)

首先有一个很显然的性质,就是这个复合函数是单调递增的。因此若 \(p\) 的前面确定,则只需最大化后面的函数值。考虑对于给定的一组 \(p_k\),将它们如何排列才能使得答案最大。

假设我们已经找到了一组最优解 \(p\)。考虑对于 \(p\) 中相邻的两项 \(i\)\(j\),其中 \(i\)\(j\) 前面,交换它们的顺序对函数值的大小有什么影响。由上面的分析,对整体函数值的影响等价于对以 \(i\) 开头的后缀的函数值的影响。而又由假设,\(j\) 之后的函数值是确定的,因此只需比较以下两个函数的大小:

\[f_i(f_j(x)), f_j(f_i(x)) \]

把它们的表达式写出来,得

\[f_i(f_j(x)) = a_i(a_j x + b_j) + b_i = a_i a_j x + a_i b_j + b_i \]

\[f_j(f_i(x)) = a_j(a_i x + b_i) + b_j = a_i a_j x + a_j b_i + b_j \]

由最优解的假设,必有 \(f_i(f_j(x)) \geq f_j(f_i(x))\)。这等价于

\[a_i b_j + b_i \geq a_j b_i + b_j \]

移项,得

\[(a_i - 1)b_j \geq (a_j - 1)b_i \]

移项,得

\[\dfrac{a_i - 1}{b_i} \geq \dfrac{a_j - 1}{b_j} \]

因此,对变量 \(i\),最优解 \(p\) 一定满足 \(\dfrac{a_i - 1}{b_i}\) 的值单调不增。即只需将给定的数组 \(\{(a_i, b_i)\}\)\(\dfrac{a_i - 1}{b_i}\) 从大到小排序,然后 01 背包即可。

时间复杂度为 \(\Theta(nk)\)

AC 代码
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N=200003,M=13;
const long long NIN=-1e17;
int n,m,a[N],b[N],c[N];
long long f[N][M];
bool cmp(const int& u,const int& v){return (a[u]-1)*b[v]<(a[v]-1)*b[u];
}
int main(){
//	freopen("composition.in","r",stdin);
//	freopen("composition.out","w",stdout);int i,j;scanf("%d%d",&n,&m);for(i=1;i<=n;i++)scanf("%d%d",&a[i],&b[i]);for(i=1;i<=n;i++) c[i]=i;sort(c+1,c+n+1,cmp);memset(f,NIN,sizeof f);f[0][0]=1;for(i=1;i<=n;i++)for(j=1,f[i][0]=1;j<=m;j++)f[i][j]=max(f[i-1][j],f[i-1][j-1]*a[c[i]]+b[c[i]]);printf("%lld",f[n][m]);
//	fclose(stdin);
//	fclose(stdout);return 0;
}

3. [ABC371G] Lexicographically Smallest Permutation

简要题意

给定序列 \((1, 2, \cdots, n)\) 的两个排列 \(A = (A_1, A_2, \cdots, A_n)\)\(C = (C_1, C_2, \cdots, C_n)\)

定义一次操作为对所有 \(i = 1, 2, \cdots, n\),将 \(C_i\) 同时替换为 \(C_{A_i}\)

你可以进行任意多次上述操作,包括 \(0\) 次。求所有能够得到的 \(C\) 序列中字典序最小的序列。

\(1 \leq n \leq 2 \times 10^5\)\(1 \leq A_i, C_i \leq n\)

考虑建出一个 \(n\) 个点、\(n\) 条边的有向图,对于每个 \(A_i\),在图中连边 \(i \rightarrow A_i\)。容易发现图中每个点的入度、出度均为 \(1\),因此这个图是由若干个并列的简单环组成的。

这么连边的意义在于我们可以很方便地找到任意次操作后序列 \(C\) 会变成什么。对于任意一个点 \(u\),设从 \(u\) 出发顺着每个点唯一的出边走 \(k\) 步后到达点 \(v\),那么经过 \(k\) 次操作后 \(C_u\) 的值即为最开始的 \(C_v\)。由此,我们称点 \(i\) 的权值为 \(C_i\)。现在问题转化成:

对于一个非负整数 \(k\),由 \(k\) 生成一个序列,序列的第 \(i\) 项为图中从点 \(i\) 出发走 \(k\) 步走到的点的点权。你需要最小化该序列的字典序。

接下来我们考虑怎么求出答案。由字典序的性质,我们需要首先保证点 \(1\) 走到的点权值最小,再保证点 \(2\) 走到的点权值最小,再保证点 \(3\) 走到的点权值最小,以此类推。因此,我们按这个顺序贪心地选取答案每一项。我们考虑现在枚举到点 \(i\),序列前 \(i - 1\) 项的答案已经确定。我们找出 \(i\) 所在的环,设这个环上编号最小的点为 \(i'\)。显然有 \(i' \leq i\)。若 \(i' < i\),确定了点 \(i'\) 走到的点,相当于整个环上所有点的答案已经确定了,因此我们无需再求一遍点 \(i\) 走到的点。

我们现在考虑 \(i' = i\) 的情况。一个容易想到的贪心是将 \(i\) 走到环上权值最小的点,但这样可能会和前面的答案矛盾。具体来说,假设有两个环,环上点的编号按边的方向依次为 \(1, 2\)\(3, 4, 5, 6\),其中 \(1\)\(4\) 的权值分别最小,那么在第一个环上你需要从 \(1\) 开始走偶数步才能走到答案,第二个环上你需要从 \(3\) 开始走奇数步才能走到答案(严格来说步数模 \(4\)\(1\)),两个环上的答案不可能同时得到。我们由此受到启发,得到了判断答案间是否出现矛盾的方法:

设当前确定了 \(m\) 个环的答案,第 \(j\) 个环的长度为 \(p_j\),每个点出发走 \(a_j\) 步第一次走到答案。那么这组答案合法当且仅当存在非负整数 \(k\) 使得对所有 \(j\),均有 \(k \equiv a_j \pmod {p_j}\)

我们只需枚举环上的点作为 \(i\) 可能的答案,再在所有合法的可能值中找出点权最小的。但问题在于 \(k\) 的大小没有限制,直接用上述方法判断显然难以接受。为了减小判断合法性的时间复杂度,我们想到将模数拆分。我们考虑中国剩余定理的一个推论:

对于正整数 \(x, y, t\)\(x \equiv y \pmod t\) 当且仅当对于所有满足 \(p\) 是质数且 \(p^a | t\) 的二元组 \((p, a)\),均有 \(x \equiv y \pmod {p^a}\)

所有这样的 \((p, a)\) 可以通过质因数分解求得。我们考虑所有环带来的限制 \(k \equiv a_j \pmod {p_j}\)(也就是上面那个式子),我们将 \(p_j\) 转化为其能分解出的 \((p, a)\) 对应的限制 \(k \equiv a_j \pmod {p^a}\)。也就是说对于所有这样的模数 \(p^a\),我们都要求有一个唯一的余数。可以用一个数组 \(f(x)\) 求出我们要求 \(k \equiv f(x) \pmod x\)。我们判断一个新加入的答案是否合法,只需考虑其对应的所有 \((p, a)\) 是否满足这个式子即可。

时间复杂度为 \(\Theta(n \sqrt n)\)

AC 代码
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int N=200003;
bool bj[N];
int n,p,a[N],c[N],f[N],ans[N];
vector<int> b[N];
int main(){
//	freopen("permutation.in","r",stdin);
//	freopen("permutation.out","w",stdout);int i,j,k,l,s1,s2,x; bool can;scanf("%d",&n);for(i=1;i<=n;i++)scanf("%d",&a[i]);for(i=1;i<=n;i++)scanf("%d",&c[i]);for(i=1;i<=n;i++){if(bj[i]) continue;b[++p].push_back(i),bj[i]=1;for(j=a[i];j!=i;j=a[j])b[p].push_back(j),bj[j]=1;}memset(f,-1,sizeof f);for(i=1;i<=p;i++){s1=n+1,s2=-1;for(j=0;j<b[i].size();j++){for(k=2,x=b[i].size(),can=1;k*k<=x;k++){if(x%k>0) continue;for(l=k;x%k==0;l*=k,x/=k)if(f[l]>=0&&j%l!=f[l]) can=0;}if(x>1&&f[x]>=0&&j%x!=f[x]) can=0;if(can&&c[b[i][j]]<s1)s1=c[b[i][j]],s2=j;}for(j=0;j<b[i].size();j++)ans[b[i][j]]=b[i][(j+s2)%b[i].size()];for(j=2,x=b[i].size();j*j<=x;j++){if(x%j>0) continue;for(k=j;x%j==0;k*=j,x/=j)f[k]=s2%k;}if(x>1) f[x]=s2%x;}for(i=1;i<=n;i++)printf("%d ",c[ans[i]]);
//	fclose(stdin);
//	fclose(stdout);return 0;
}

4. [ABC372G] Ax + By < C

简要题意

给定三个长为 \(n\) 的序列 \(A = (A_1, A_2, \cdots, A_n), B = (B_1, B_2, \cdots, B_n), C = (C_1, C_2, \cdots, C_n)\)。你需要求出满足以下条件的二元组 \((x, y)\) 的数量:

  • \(x, y\) 都是正整数。
  • 对每个 \(1 \leq i \leq n\),都有 \(A_i x + B_i y < C_i\)

单个测试点内有 \(T\) 组测试数据,你需要对每组数据都求出答案。可以证明这样的二元组数量一定是有限的。

\(1 \leq T, n, \sum n \leq 2 \times 10^5\)\(1 \leq A_i, B_i, C_i \leq 10^9\)

对于限制 \(A_i x + B_i y < C_i\),变形得 \(y \leq -\dfrac{A_i}{B_i} x + \dfrac{C_i - 1}{B_i}\),我们发现这类似于直线的解析式。考虑把这些条件画到平面直角坐标系上,那么一个条件相当于钦定第一象限上的点 \((x, y)\) 必须在某条直线上或在这条直线的下方。也就是说,每个条件都限制了 \((x, y)\) 必须在某个半平面上。为了综合这些限制,我们求一遍半平面交,相当于求出了这些直线围成的下凸壳(再加上两条坐标轴)。

显然对于直线 \(x = x_0\),若该直线与凸壳交点的纵坐标为 \(y_0\),那么横坐标为 \(x_0\) 的点就有 \(\lfloor y_0 \rfloor\) 个。我们考虑分别对于凸壳上的每一段求出其下方的点对数量,令第 \(i\) 段为 \(y = -\dfrac{a_i}{b_i} x + \dfrac{c_i - 1}{b_i}(l_i < x \leq r_i)\),那么将两个公式结合起来即可得到答案为

\[ \sum_{k = l_i + 1}^{r_i} \lfloor \dfrac{-a_i k + c_i - 1}{b_i} \rfloor = \sum_{k = 1}^{r_i - l_i} \lfloor \dfrac{-a_i k + l_i k + c_i - 1}{b_i} \rfloor \]

我们发现这个式子满足 floor-sum 算法的要求,因此只需调用 \(f(-a_i, l_i k + c_i - 1, b_i, r_i - l_i)\) 即可。时间复杂度为 \(\Theta(\sum n \cdot \log V)\)

AC 代码
#include <algorithm>
#include <cstdio>
using namespace std;
const int N=200003;
const long long PIN=4557430888798830399;
struct Line{int a,b,c; // y = -(a / b) * x + (c / b)
}a[N],b[N];
const Line era={0,1,0};
int n,m; long long c[N];
bool cmp(const Line& l1,const Line& l2){return (long long)l1.a*l2.b<(long long)l1.b*l2.a;
}
long long sec(Line l1,Line l2){// It is guaranteed that a1 / b1 <= a2 / b2.if((long long)l1.c*l2.b> (long long)l1.b*l2.c) return -1;if((long long)l1.a*l2.b==(long long)l1.b*l2.a) return PIN;long long sa=(long long)l1.b*l2.a-(long long)l1.a*l2.b;long long sb=(long long)l1.b*l2.c-(long long)l1.c*l2.b;return sb/sa;
}
int floor(int a,int b){return (a>=0)?(a/b):-((-a+b-1)/b);
}
long long f(int a,int b,int c,int n){// Calculate the sum of (a * i + b) / c(1 <= i <= n).long long sa=floor(a,c),sb=floor(b,c);long long ans=sa*n*(n+1)/2+sb*n;a-=sa*c,b-=sb*c; if(a==0||n==0) return ans;return ((long long)a*n+b)/c*n-f(c,-b-1,a,((long long)a*n+b)/c)+ans;
}
int main(){
//	freopen("line.in","r",stdin);
//	freopen("line.out","w",stdout);int i,t; long long ans;for(scanf("%d",&t);t>0;t--){scanf("%d",&n),m=0;for(i=1;i<=n;i++)scanf("%d%d%d",&a[i].a,&a[i].b,&a[i].c),a[i].c--;sort(a+1,a+n+1,cmp);for(i=1;i<=n;i++){while(m>0&&sec(b[m],a[i])==-1) m--;while(m>1&&sec(b[m],a[i])<=sec(b[m-1],b[m])) m--;b[++m]=a[i];}while(m>1&&sec(era,b[m-1])<=sec(b[m-1],b[m])) m--;for(i=1;i<m;i++)c[i]=sec(b[i],b[i+1]);c[m]=sec(era,b[m]),ans=0;for(i=1;i<=m;i++){ans+=f(-b[i].a,b[i].c,b[i].b,c[i  ]);ans-=f(-b[i].a,b[i].c,b[i].b,c[i-1]);}printf("%lld\n",ans);}
//	fclose(stdin);
//	fclose(stdout);return 0;
}

5. [ABC373G] No Cross Matching

简要题意

给定两个长为 \(n\) 的点集 \(P = \{ P_1, P_2, \cdots, P_n \}, Q = \{ Q_1, Q_2, \cdots, Q_n \}\),保证任意三点不共线。以这些点为基础建立一个平面直角坐标系,则每个点的坐标都是已知的。

现在我们在这些点间连 \(n\) 条无向边(即线段),每条边从一个 \(P\) 中的点连向一个 \(Q\) 中的点。称一种方案为一个“匹配”当且仅当图中每个点的度数均为 \(1\)。定义一个匹配“合法”当且仅当我们连出的任意两条线段都不想交。

你需要构造一个合法的匹配,或报告无解。

\(1 \leq n \leq 300\)\(1 \leq a_i \leq 5000\)

我们定义一个匹配的权值为其中所有线段的长度之和。那么我们断言:权值最小的匹配一定合法。因为对于一个不合法的匹配,考虑两条相交的线段(不妨设为 \(P_1Q_1\)\(P_2Q_2\),记它们交于点 \(O\));根据三角形的三边关系,必有 \(OP_1 + OQ_2 > P_1Q_2, OQ_1 + OP_2 > P_2Q_1\),将两式的三项对应相加得 \(P_1Q_1 + P_2Q_2 > P_1Q_2 + P_2Q_1\);因此,如果将这两条线段改为 \(P_1Q_2\)\(P_2Q_1\),显然这仍然是一个匹配且线段总长度更小。所以,不合法的匹配一定不是权值最小的,因此权值最小的匹配一定合法。

问题转化为如何找出权值最小的匹配,我们发现这可以用费用流解决。具体地,从原点向每个 \(P_i\) 连容量为 \(1\)、费用为 \(0\) 的边;从每个 \(Q_j\) 向汇点连容量为 \(1\)、费用为 \(0\) 的边;从每个 \(P_i\) 向每个 \(Q_j\) 连容量为 \(1\)、费用为线段 \(P_iQ_j\) 的长度的边(费用可能为浮点数)。显然有解当且仅当该图的最大流为 \(n\);构造方案只需考虑跑完网络流后所有 \(P_i\) 连向 \(Q_j\) 的边,若流量为 \(1\) 则说明在匹配中。

因此我们只需建图后跑一遍最小费用最大流。然而常用的 SSP 算法复杂度为 \(\Theta(fnm) = \Theta(n^4)\),无法接受。你需要使用复杂度更为优秀的 Primal-Dual 算法,在 \(\Theta(nm + fn^2) = \Theta(n^3)\) 的时间复杂度内通过此题。

AC 代码
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <utility>
#define x first
#define y second
using namespace std;
const int P=303,N=P*2,M=P*P+N;
const int PIN=1061109567;
int n,to[P][P];
int len_list=0,e[M*2],ne[M*2],h[N];
pair<int,double> w[M*2];
pair<int,int> a[P],b[P];
double get_dist(pair<int,int> u,pair<int,int> v){return sqrt((u.x-v.x)*(u.x-v.x)+(u.y-v.y)*(u.y-v.y));
}
int dotl(int u){ return u  ; }
int dotr(int u){ return u+n; }
void add(int a,int b,pair<int,double> val){e[len_list]=b;w[len_list]=val;ne[len_list]=h[a];h[a]=len_list++;
}
void add_once(int a,int b,pair<int,double> val){add(a,b,val);add(b,a,make_pair(0,-val.y));
}
void Bellman_Ford(int S,double pot[N]){int i,j; double ls[N];for(i=0;i<=n*2+1;i++)pot[i]=PIN;pot[S]=0;for(i=1;i<=n*2+1;i++){for(j=0;j<=n*2+1;j++) ls[j]=pot[j];for(j=0;j<len_list;j+=2)if(w[j].x>0&&ls[e[j^1]]+w[j].y<pot[e[j]])pot[e[j]]=ls[e[j^1]]+w[j].y;}
}
bool Dijkstra(int S,int T,double pot[N],int pre[N],int la[N],double& val){bool bj[N]={0}; int i,j,s2;double s1,dis[N];for(i=0;i<=n*2+1;i++)dis[i]=PIN,pre[i]=la[i]=-1;for(i=0,dis[S]=0;i<=n*2+1;i++){s1=PIN,s2=-1;for(j=0;j<=n*2+1;j++)if(!bj[j]&&dis[j]<s1)s1=dis[j],s2=j;if(s2==-1) break;for(j=h[s2],bj[s2]=1;j>=0;j=ne[j])if(w[j].x>0&&dis[s2]+w[j].y+pot[s2]-pot[e[j]]<dis[e[j]]){dis[e[j]]=dis[s2]+w[j].y+pot[s2]-pot[e[j]];pre[e[j]]=s2,la[e[j]]=j;}}val=dis[T]-pot[S]+pot[T];for(i=0;i<=n*2+1;i++) pot[i]+=dis[i];return dis[T]<PIN/2;
}
pair<int,double> Primal_Dual(int S,int T){double pot[N];pair<int,double> ans(0,0);Bellman_Ford(S,pot);while(true){int pre[N]={0},la[N]={0},i;pair<int,double> s1(PIN,0);if(!Dijkstra(S,T,pot,pre,la,s1.y)) break;for(i=T;i!=S;i=pre[i])s1.x=min(s1.x,w[la[i]].x);s1.y*=s1.x,ans.x+=s1.x,ans.y+=s1.y;for(i=T;i!=S;i=pre[i])w[la[i]].x-=s1.x,w[la[i]^1].x+=s1.x;}return ans;
}
int main(){
//	freopen("matching.in","r",stdin);
//	freopen("matching.out","w",stdout);int i,j;scanf("%d",&n);for(i=1;i<=n;i++)scanf("%d%d",&a[i].x,&a[i].y);for(i=1;i<=n;i++)scanf("%d%d",&b[i].x,&b[i].y);memset(h,-1,sizeof h);for(i=1;i<=n;i++)for(j=1;j<=n;j++){to[i][j]=len_list;add_once(dotl(i),dotr(j),make_pair(1,get_dist(a[i],b[j])));}for(i=1;i<=n;i++)add_once(0,dotl(i),make_pair(1,0));for(i=1;i<=n;i++)add_once(dotr(i),n*2+1,make_pair(1,0));if(Primal_Dual(0,n*2+1).x<n)printf("-1");elsefor(i=1;i<=n;i++)for(j=1;j<=n;j++)if(w[to[i][j]^1].x==1)printf("%d ",j);
//	fclose(stdin);
//	fclose(stdout);return 0;
}

6. [ABC376G] Treasure Hunting

简要题意

给定一棵 \(n + 1\) 个点的树,点编号从 \(0\)\(n\)。根结点为 \(0\),点 \(i(1 \leq i \leq n)\) 的父亲为 \(p_i\),且有点权 \(a_i\)

树上有一个非根结点藏了宝藏,但你不知道宝藏具体在哪个结点。你只知道宝藏在点 \(i\) 的概率为 \(\dfrac{a_i}{\sum_{k = 1}^n a_k}\)

为了找到宝藏,你需要按顺序搜索树上的结点。初始时你在 \(0\) 号结点,标志着你已经搜索过根结点。你需要不断进行如下操作,直到搜到宝藏为止:

  • 选择一个未搜索过的结点 \(u\),满足 \(u\) 的父亲 \(p_u\) 已经被搜索过;然后搜索结点 \(u\)

你想尽快找到宝藏。因此你想知道,若你按照最优策略进行搜索,操作次数的期望值最小是多少。答案对 \(998244353\) 取模。

每个测试点内有 \(T\) 组测试数据。

\(1 \leq T, n, \sum n \leq 2 \times 10^5\)\(a_i \geq 1\)\(\sum a_i \leq 10^8\)

发现最优策略一定形如:我们按照一个固定的顺序搜索整棵树。令这个顺序为 \(s_1, s_2, \cdots, s_n\),记 \(X = \sum_{k = 1}^n a_k\),由期望可加性可知答案为 \(X + (X - a_{s_1}) + (X - a_{s_1} - a_{s_2}) + \cdots + (X - a_{s_1} - a_{s_2} - \cdots - a_{s_n})\) 的值再除以 \(X\)。由于除数是个常数,我们不妨忽略它,下文的“答案”即指这里的被除数。

如果选择点 \(u\) 时,它的父亲选不选都无所谓,那么相当于我们可以任意钦定搜索结点的顺序。考虑将答案化简为 \(nX - (n - 1)a_{s_1} - (n - 2)a_{s_2} - \cdots - 1 \cdot a_{s_{n - 1}} - 0 \cdot a_{s_n}\)。由排序不等式,\(a_i\) 越大的结点应该越先搜索。因此我们得出结论,每一轮操作中,我们总是选择剩余结点中 \(a_i\) 最大的进行搜索

然而这个顺序不能任意钦定,搜索必须从根往下依次进行,具体地,我们定义一个结点能够搜索当且仅当它的父亲已被搜索。考虑扩展上述结论,维护一个集合 \(S\) 表示当前能够搜索的结点,那么存在如下贪心策略:每一轮操作中,我们总是选择集合 \(S\)\(a_i\) 最大的进行搜索。这个贪心显然是错误的,因为我们很容易构造出反例:如果 \(S\) 中某个结点 \(u\) 的点权很小,但是 \(u\) 的某个儿子 \(v\) 的点权接近无穷大,那么我们为了较早地选 \(v\) 显然应该优先选择点 \(u\)。由此我们得出这个贪心结论的适用范围:对于每一点 \(u(p_u > 0)\),都有 \(a_{p_u} \geq a_u\)(因为这种情况下,按贪心策略得出的顺序与任意钦定选择顺序得出的最优策略相同)。

那么如果存在 \(a_u > a_{p_u}\) 的情况怎么办?不妨设 \(u\)\(p_u\) 的儿子中点权最大的,由于我们抛弃上述贪心策略而先选 \(p_u\) 的目的就是选择点 \(u\),因此搜索 \(p_u\) 后我们就应该立即搜索点 \(u\)。由此我们不妨将 \(p_u\)\(u\) 合并(注意这里是有序的),合并后的结点 \(p_u - u\) 表示:在最优选择序列中 \(p_u\)\(u\) 应该是连续的,且 \(p_u\)\(u\) 之前被选择。由此,我们得出本题的正确思路:

定义广义结点 \(s_1 - s_2 - \cdots - s_k\) 为原树上结点 \(s_1, s_2, \cdots, s_k\) 合并形成的新结点,表示在最优选择序列中这些结点所在的下标是连续的,且其内部顺序为 \(s_1, s_2, \cdots, s_k\)。我们为每个广义结点分配一个编号,定义广义结点 \(u\) 表示编号为 \(u\) 的广义结点。

对于树上所有广义结点,我们约定“任意钦定选择顺序”必须符合广义结点的定义,即其包含的结点下标连续且内部有序。我们定义广义结点 \(u\)\(v\) 优当且仅当若任意钦定选择顺序,其他结点顺序不变时,先选 \(u\) 中的结点得到的答案小于先选 \(v\) 中的结点得到的答案。

对于本题,如果存在一个广义结点 \(v\) 满足 \(v\) 优于它的父亲 \(u\),那么合并 \(u\)\(v\)。不断合并结点直到所有的广义结点都劣于它的父亲。然后我们按如下贪心策略进行:维护集合 \(S\) 表示当前能够搜索的广义结点,每一轮操作中,我们总是选择 \(S\) 中最优的广义结点进行搜索

剩下的问题为:如何判定两个广义结点谁优谁劣,以及按什么顺序合并结点。

对于两个广义结点 \(u\)\(v\),我们考虑任意钦定选择顺序后的一种选择顺序,满足 \(u\)\(v\) 前面且 \(u\)\(v\)。记 \(u\) 为广义结点 \(s_1 - s_2 - \cdots - s_p\)\(v\)\(t_1 - t_2 \cdots - t_q\)\(u\) 前面所有结点的点权和为 \(X - x\)。那么

  • 按原顺序,先选 \(u\) 后选 \(v\) 的值为:

\[\begin{align*}&x + (x - a_{s_1}) + (x - a_{s_1} - a_{s_2}) + \cdots + (x - a_{s_1} - a_{s_2} - \cdots - a_{s_{p - 1}}) \\ + &(x - \sum a_{s} - a_{t_1}) + (x - \sum a_{s} - a_{t_1} - a_{t_2}) + \cdots + (x - \sum a_{s} - a_{t_1} - a_{t_2} - \cdots - a_{t_{q - 1}}) \\ + &B = A + B - q \sum a_{s} \end{align*} \]

  • 交换顺序,先选 \(v\) 后选 \(u\) 的值为:

\[\begin{align*}&x + (x - a_{t_1}) + (x - a_{t_1} - a_{t_2}) + \cdots + (x - a_{t_1} - a_{t_2} - \cdots - a_{t_{q - 1}}) \\ + &(x - \sum a_{t} - a_{s_1}) + (x - \sum a_{t} - a_{s_1} - a_{s_2}) + \cdots + (x - \sum a_{t} - a_{s_1} - a_{s_2} - \cdots - a_{s_{p - 1}}) \\ + &B = A + B - p \sum a_{t} \end{align*} \]

其中 \(A\)\(B\) 是在两式中不变的值。若要让前者小于后者,则有 \(q \sum a_{s} > p \sum a_{t}\),即 \(\dfrac{\sum a_{s}}{p} > \dfrac{\sum a_{t}}{q}\)。因此,我们定义广义结点 \(u\) 的权值为\(u\) 中结点的权值和除以 \(u\) 中结点的个数,那么广义结点 \(u\) 优于 \(v\) 当且仅当 \(u\) 的权值比 \(v\) 大。具体地,对于每个广义结点,我们只需维护三个值:其包含的结点个数,结点的权值和,结点权值前缀和的和。

接下来考虑合并结点的顺序。考虑维护集合 \(S\) 表示在之后的合并操作中,\(S\) 以外的点都不会作为儿子进行合并。每轮合并操作时,作为儿子的广义结点 \(u\) 需要满足如下条件:

  • 若合并操作能够进行(\(u\) 优于父亲),则 \(u\) 一定是所有兄弟中权值最大的。
  • 若合并操作不能进行(\(u\) 不优于父亲),为保证时间复杂度(可以直接在 \(S\) 中删去 \(u\)),此后不能存在任意时刻使得 \(u\) 优于父亲。

对于第一条限制,我们只需找出权值最大的点 \(u\) 进行合并即可。对于第二条限制,若 \(u\) 严格劣于父亲,则说明父亲不在 \(S\) 中,\(u\) 符合条件;若 \(u\) 和父亲的点权相等,为减小判断难度,我们应选择父亲进行合并而不是 \(u\)。因此我们按如下策略进行合并:每次找到 \(S\) 中权值最大的点进行合并。若有多个点符合条件,取 dfs 序最小的

于是我们解决了这道题。实现时,合并过程和求答案过程中的集合 \(S\) 都可以用堆维护。对于合并两个结点的儿子的操作,使用启发式合并即可。总时间复杂度为 \(\Theta(\sum n \cdot \log n)\)

AC 代码
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
const int N=200003,mod=998244353;
struct Node{int cnt,sum;long long tot;
}a[N];
bool ban[N];
int n,fa[N],dfn[N];
vector<int> e[N];
bool cmp(int u,int v){if((long long)a[u].sum*a[v].cnt!=(long long)a[v].sum*a[u].cnt)return (long long)a[u].sum*a[v].cnt>(long long)a[v].sum*a[u].cnt;return dfn[u]<dfn[v];
}
int pow(int a,int b){int ans=1;while(b>0){if(b&1) ans=(long long)ans*a%mod;a=(long long)a*a%mod; b>>=1;}return ans;
}
void dfs(int u,int& p){dfn[u]=++p;for(int i=0;i<e[u].size();i++)dfs(e[u][i],p);
}
namespace Heap{int n,a[N],p[N];void up(int u){while(u>1&&cmp(a[u],a[u/2])){swap(p[a[u]],p[a[u/2]]);swap(a[u],a[u/2]),u/=2;}}void down(int u){while(u*2<=n&&cmp(a[u*2],a[u])||u*2<n&&cmp(a[u*2+1],a[u]))if(u*2==n||cmp(a[u*2],a[u*2+1])){swap(p[a[u]],p[a[u*2]]);swap(a[u],a[u*2]),u*=2;}else{swap(p[a[u]],p[a[u*2+1]]);swap(a[u],a[u*2+1]),u=u*2+1;}}void push(int u){a[++n]=u; p[u]=n; up(n);}void pop(){if(!n) return ;p[a[n]]=1; p[a[1]]=0;a[1]=a[n--]; down(1);}void erase(int u){if(!p[u]) return ;p[a[n]]=p[u]; a[p[u]]=a[n--];up(p[u]),down(p[u]); p[u]=0;}bool empty(){ return !n; }int top(){ return a[1]; }
}
int merge(int u,int v){// It is guaranteed that "u" is the father of "v".if(e[u].size()>=e[v].size()){a[u].tot+=a[v].tot+(long long)a[u].sum*a[v].cnt;a[u].cnt+=a[v].cnt,a[u].sum+=a[v].sum;for(int i=0;i<e[v].size();i++)if(!ban[e[v][i]]) fa[e[v][i]]=u;for(int i=0;i<e[v].size();i++)if(!ban[e[v][i]]) e[u].push_back(e[v][i]);ban[v]=1,e[v].clear(); return u;}else{a[v].tot+=a[u].tot+(long long)a[u].sum*a[v].cnt;a[v].cnt+=a[u].cnt,a[v].sum+=a[u].sum;for(int i=0;i<e[u].size();i++)if(!ban[e[u][i]]&&e[u][i]!=v) fa[e[u][i]]=v;for(int i=0;i<e[u].size();i++)if(!ban[e[u][i]]&&e[u][i]!=v)e[v].push_back(e[u][i]);fa[v]=fa[u],e[fa[u]].push_back(v);ban[u]=1,e[u].clear(); return v;}
}
int main(){
//	freopen("treasure.in","r",stdin);
//	freopen("treasure.out","w",stdout);int i,t,sum,s1,cur; long long ans;for(scanf("%d",&t);t>0;t--){scanf("%d",&n);for(i=0;i<=n;i++)e[i].clear(),ban[i]=0;for(i=1;i<=n;i++){scanf("%d",&fa[i]);e[fa[i]].push_back(i);}a[0]={0,0,0};for(i=1,sum=0;i<=n;i++){scanf("%d",&a[i].sum);a[i].cnt=1,a[i].tot=0;sum+=a[i].sum;}dfs(0,s1=0);for(i=1;i<=n;i++) Heap::push(i);while(!Heap::empty()){s1=Heap::top(),Heap::pop();if(fa[s1]&&cmp(s1,fa[s1])){Heap::erase(fa[s1]);Heap::push(merge(fa[s1],s1));}}Heap::push(0),ans=0,cur=sum;while(!Heap::empty()){s1=Heap::top(),Heap::pop();ans+=(long long)cur*a[s1].cnt-a[s1].tot;for(cur-=a[s1].sum,i=0;i<e[s1].size();i++)if(!ban[e[s1][i]]) Heap::push(e[s1][i]);}printf("%lld\n",ans%mod*pow(sum,mod-2)%mod);}
//	fclose(stdin);
//	fclose(stdout);return 0;
}

7. [ABC386G] Many MST

简要题意

考虑一类 \(n\) 个点的无向完全图,其中每条边的权值都在 \([1, m]\) 中。显然这样的图有 \(m^{\frac{n(n - 1)}{2}}\) 张。

对连通图 \(G_0\),记 \(W(G_0)\) 表示图 \(G_0\) 的最小生成树上的边权和。你需要对上述所有图 \(G\) 求出 \(w(G)\) 之和,答案对 \(998244353\) 取模。

\(2 \leq n \leq 500\)\(1 \leq m \leq 500\)

感觉前面的转化很难想,但想通了后面的 DP 就容易了。

在本文中,我们约定 \(C(G)\) 表示 \(G\) 的所有连通块的点集构成的集合,\(E(G)\) 表示 \(G\) 的边集,\(T(G)\) 表示 \(G\) 的最小生成树(森林);\(S\) 表示所有满足条件的 \(G\) 构成的集合,\(G_{P}\) 表示只保留 \(G\) 所有点和满足条件 \(P\) 的边构成的新图。那么显然有 \(T_{w \leq x}(G) = T(G_{w \leq x})\)

套路地,我们考虑拆贡献,对一条最小生成树上的边 \(e\),将 \(w(e)\) 拆成 \(\sum_{x = 0}^{m - 1} [w(e) > x]\)。这样做的好处是我们可以将每条边的每一项和在一起算,于是问题就简化为求最小生成森林上的边数。具体地,有

\[\begin{align*} \sum_{G \in S} W(G) &= \sum_{G \in S} \sum_{e \in T(G)} w(e) = \sum_{G \in S} \sum_{x = 0}^{m - 1} \sum_{e \in T(G)} [w(e) > x] = \sum_{G \in S} \sum_{x = 0}^{m - 1} |E(T_{w > x}(G))| \\ &= \sum_{G \in S} \sum_{x = 0}^{m - 1} (n - 1 - |E(T_{w \leq x}(G))|) = \sum_{G \in S} \sum_{x = 0}^{m - 1} (n - 1 - |E(T(G_{w \leq x}))|) \end{align*} \]

接下来是本题最难的地方:将最小生成森林的边数转化为森林的连通块数。为什么要这么做呢?首先,一棵树的边数与连通块数有简单的关系;其次,对于一张图,其生成森林的连通块数与它本身的连通块数是相等的。这么转化后我们就不用再非常麻烦地求最小生成森林,而可以直接在原图上求解。具体地,有

\[\begin{align*} \sum_{G \in S} \sum_{x = 0}^{m - 1} (n - 1 - |E(T(G_{w \leq x}))|) &= \sum_{G \in S} \sum_{x = 0}^{m - 1} (|C(T(G_{w \leq x}))| - 1) = \sum_{G \in S} \sum_{x = 0}^{m - 1} (|C(G_{w \leq x})| - 1) \\ &= \sum_{G \in S} (\sum_{x = 0}^{m - 1} |C(G_{w \leq x})| - m) = \sum_{x = 0}^{m - 1} \sum_{G \in S} |C(G_{w \leq x})| - m^{\frac{n(n - 1)}{2} + 1} \end{align*} \]

于是问题转化为:定义 \(u, v\) 连通当且仅当从 \(u\) 出发只经过边权不超过 \(x\) 的边到达 \(v\),求所有图的连通块数量之和。然而直接 DP 还是不好做。考虑继续拆贡献,我们枚举每个点集 \(V\),判断它是否为图 \(G\) 的连通块。具体地,设图 \(G\) 的点集为 \(I = {1, 2, \cdots, n}\),有

\[\sum_{G \in S} |C(G_{w \leq x})| = \sum_{G \in S} \sum_{V \subseteq I} [V \subseteq C(G_{w \leq x})] = \sum_{V \subseteq I} \sum_{G \in S} [V \in C(G_{w \leq x})] \]

这样做的好处就很明显了。由于图 \(G\) 是完全图,因此任意两个大小相等的点集 \(V\),它们的贡献是一样的。于是我们只需枚举点集的大小,即

\[\sum_{V \subseteq I} \sum_{G \in S} [V \in C(G_{w \leq x})] = \sum_{i = 1}^n \binom{n}{i} \sum_{G \in S} [\{1, 2, \cdots, i\} \in C(G_{w \leq x})] \]

答案即为:

\[\sum_{G \in S} W(G) = \sum_{x = 0}^{m - 1} \sum_{i = 1}^n \binom{n}{i} \sum_{G \in S} [\{1, 2, \cdots, i\} \in C(G_{w \leq x})] - m^{\frac{n(n - 1)}{2} + 1} \]

我们要求图 \(G_{w \leq x}\) 恰有一个连通块为 \(1, 2, \cdots, i\),求图 \(G\) 的数量。事实上,我们只需求出由这 \(i\) 个点组成的连通分量的数量 \(t\),答案即为 \(t(m - x)^{i(n - i)} m^{\frac{(n - i)(n - i - 1)}{2}}\)(要求剩余点与前 \(i\) 个点之间的边权均大于 \(x\),剩余点之间边权随便)。我们发现求 \(t\) 的过程与下面的经典问题类似:

如何求出 $n$ 个点的简单连通图的数量?

考虑 DP,设 \(f(i)\) 表示 \(i\) 个点的简单连通图的数量。转移使用容斥原理进行,显然简单图的总数为 \(2^{\frac{n(n - 1)}{2}}\);为扣去不连通图的贡献,我们枚举 \(j\),判断有多少种简单图满足 \(1\) 所在的连通块点数为 \(j\)。显然这样的连通块有 \(\binom{i - 1}{j - 1}\) 种,此时我们额外要求:这 \(j\) 个点之间连通,与外界的点不连通。于是有转移

\[f(i) = 2^{\frac{n(n - 1)}{2}} - \sum_{j = 1}^{i - 1} 2^{\frac{(i - j)(i - j - 1)}{2}} \binom{i - 1}{j - 1} f(j) \]

想必大家都已经会了,本题只需加上边权带来的影响即可。时间复杂度为 \(\Theta(mn^2)\)

AC 代码
#include <cstdio>
const int N=503,mod=998244353;
int n,m,f[N],powm[N*N],powi[N*N],C[N][N];
int main(){
//	freopen("MST.in","r",stdin);
//	freopen("MST.out","w",stdout);int i,j,k,s1,ans=0;scanf("%d%d",&n,&m);for(i=1,powm[0]=1;i<=n*(n-1)/2+1;i++)powm[i]=(long long)powm[i-1]*m%mod;for(i=0;i<=n;i++)for(j=1,C[i][0]=C[i][i]=1;j<i;j++)C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;for(i=0;i<m;i++){for(j=1,powi[0]=1;j<=n*(n-1)/2;j++)powi[j]=(long long)powi[j-1]*(m-i)%mod;for(j=1;j<=n;j++)for(k=1,f[j]=powm[j*(j-1)/2];k<j;k++){s1=(long long)f[k]*powi[k*(j-k)]%mod*powm[(j-k)*(j-k-1)/2]%mod;f[j]=(f[j]-(long long)s1*C[j-1][k-1]%mod+mod)%mod;}for(j=1;j<=n;j++){s1=(long long)f[j]*powi[j*(n-j)]%mod*powm[(n-j)*(n-j-1)/2]%mod;ans=(ans+(long long)s1*C[n][j]%mod)%mod;}}ans=(ans-powm[n*(n-1)/2+1]+mod)%mod;printf("%d",ans);
//	fclose(stdin);
//	fclose(stdout);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/860812.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GIMP 处理 png 透明边缘

首先设置画布大小然后点击 图层 -> 图层到图像大小

时间的本质-计算视角

原作:Stephen Wolfram计算机视域下的时间 时间是人类经验的核心。然而,究竟什么是时间?在传统科学中,它常被比作空间坐标(尽管这种坐标对我们来说总是不断增长)。尽管这种描述在数学上可能很有用,但它并没有揭示时间的本质。 一旦我们开始用计算术语思考,就会觉得越来越…

中考英语优秀范文-002 Music 音乐

1 写作要求 作家雨果说过:“开启人类智慧的钥匙有三把,一是字母,二是数字,三是音符。”从这句话中,我们足可见音乐对人的发展的影响。请你根据以下提示,以Music为题,写一篇短文参加学校的英语作文竞赛。 提示: 1音乐使人充满活力,让人快乐; 2 没有音乐,生活就没有乐…

Windows11安装Linux子系统(WSL2)

1、确认BIOS中已经打开虚拟化 Virtualization Technology (我的机器默认已经打开 Enabled / Disabled,主板不同进入的地方不一样,自行搜索) 2、以管理员身份打开 PowerShell 3、输入(此步安装WSL): dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subs…

软工个人总结

结束了? 一、学期回顾 1.1 课程想象与现实 最初听闻软件工程课程,脑海中浮现的是一群程序员在电脑前敲打着代码,创造出一个个功能强大的软件。想象着自己能通过这门课,精通多种编程语言,独立开发出令人惊艳的应用程序。可是在第一次编程作业布置下来时,发现自己什么也不会…

UML之关联

关联指两个类之间的各种联系。UML使用各种单实线表示关联,但关联所能够表达的信息远不只是一条实线所传递的依赖。关联指两个类之间的各种联系。UML使用各种单实线表示关联,这个单实线可以是直线(垂直的、水平的或者倾斜的)、折线甚至曲线。 事实上,关联也是展示类的属性的…

代码随想录——动态规划13.分割等和子集

思路 难点 我只想到了:“找一个子集,每个数取或不取求其和,看是否和另一个子集的和相等 ” 但是实际上既然是两个子集相等,那么只要和等于 sum/2 即可了! 取或不取用01背包,但是不知道怎么用。 只有确定了如下四点,才能把01背包问题套到本题上来。背包的体积为sum / 2 背…

深度解析 Transformer 模型中的位置嵌入(Positional Embedding)

在自然语言处理中,词语的顺序对句子的意义至关重要。然而,传统的自注意力机制无法区分词语的位置。本文深入浅出地介绍了**位置嵌入(Positional Embedding)**的概念及其在Transformer模型中的作用,解释了它如何帮助模型理解词语的顺序,从而提升文本处理的准确性。通过简单…

java8--方法--格式化输出--printf--索引

System.out.printf("%1$s %2$tB %2$te %2$tY","Due date",new Date()); 效果图:ps: 1.一个字符串需要有多个格式化单词,通过建立索引实现,索引值用%$包围,$后紧跟格式化的目标类型,后面按顺序传入变量或填写内容 2.t指定日期类型,b指定填充月份的完…

前端重学之Number

Number (尾附IEEE754解读) mdn文档 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Numberjs里的number是双进度浮点数 用IEEE745 编码 0b 0o 0x 分别表示 二进制 八进制 十六进制```js 0.tostring() //报错 0 .tostring()//正确 ```IEEE7…

DVWA靶场搭建及错误解决教程

前言 DVWA(Damn Vulnerable Web Application)靶场是一个旨在帮助安全人员和开发人员学习和提高网络安全技能的开源项目。它是一个故意存在多种安全漏洞的 PHP/MySQL 网络应用程序,通常用于学习和测试各种网络攻击技术 工具下载链接:https://pan.quark.cn/s/49ef556eb32b 搭…

招行面试:万亿GB网盘, 从0到1设计,如何实现?

本文原文链接 文章很长,且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录 博客园版 为您奉上珍贵的学习资源 : 免费赠送 :《尼恩Java面试宝典》 持续更新+ 史上最全 + 面试必备 2000页+ 面试必备 + 大厂必备 +涨薪必备 免费赠送 :《尼恩技术圣经+高并发系列PDF》 ,帮你 …